summaryrefslogtreecommitdiffstats
path: root/starmath/source
diff options
context:
space:
mode:
Diffstat (limited to 'starmath/source')
-rw-r--r--starmath/source/ElementsDockingWindow.cxx687
-rw-r--r--starmath/source/SmElementsPanel.cxx102
-rw-r--r--starmath/source/SmElementsPanel.hxx56
-rw-r--r--starmath/source/SmPanelFactory.cxx136
-rw-r--r--starmath/source/SmPropertiesPanel.cxx92
-rw-r--r--starmath/source/SmPropertiesPanel.hxx54
-rw-r--r--starmath/source/accessibility.cxx745
-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.cxx1474
-rw-r--r--starmath/source/cursor.cxx1606
-rw-r--r--starmath/source/dialog.cxx2170
-rw-r--r--starmath/source/document.cxx1583
-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.cxx155
-rw-r--r--starmath/source/mathml/attribute.cxx479
-rw-r--r--starmath/source/mathml/def.cxx124
-rw-r--r--starmath/source/mathml/element.cxx136
-rw-r--r--starmath/source/mathml/export.cxx1090
-rw-r--r--starmath/source/mathml/import.cxx1405
-rw-r--r--starmath/source/mathml/iterator.cxx77
-rw-r--r--starmath/source/mathml/mathmlMo.cxx1128
-rw-r--r--starmath/source/mathml/mathmlattr.cxx165
-rw-r--r--starmath/source/mathml/mathmlexport.cxx1443
-rw-r--r--starmath/source/mathml/mathmlimport.cxx2673
-rw-r--r--starmath/source/mathml/starmathdatabase.cxx794
-rw-r--r--starmath/source/mathml/xparsmlbase.cxx2166
-rw-r--r--starmath/source/mathtype.cxx3347
-rw-r--r--starmath/source/mathtype.hxx184
-rw-r--r--starmath/source/node.cxx2491
-rw-r--r--starmath/source/ooxmlexport.cxx596
-rw-r--r--starmath/source/ooxmlexport.hxx46
-rw-r--r--starmath/source/ooxmlimport.cxx685
-rw-r--r--starmath/source/ooxmlimport.hxx51
-rw-r--r--starmath/source/parse.cxx52
-rw-r--r--starmath/source/parse5.cxx2823
-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.cxx85
-rw-r--r--starmath/source/smediteng.cxx158
-rw-r--r--starmath/source/smmod.cxx238
-rw-r--r--starmath/source/symbol.cxx309
-rw-r--r--starmath/source/tmpdevice.cxx68
-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.cxx1109
-rw-r--r--starmath/source/utility.cxx245
-rw-r--r--starmath/source/view.cxx2239
-rw-r--r--starmath/source/visitors.cxx2648
-rw-r--r--starmath/source/wordexportbase.cxx184
-rw-r--r--starmath/source/wordexportbase.hxx48
60 files changed, 41007 insertions, 0 deletions
diff --git a/starmath/source/ElementsDockingWindow.cxx b/starmath/source/ElementsDockingWindow.cxx
new file mode 100644
index 0000000000..b00e927172
--- /dev/null
+++ b/starmath/source/ElementsDockingWindow.cxx
@@ -0,0 +1,687 @@
+/* -*- 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 <utility>
+#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>
+
+namespace
+{
+// element, element help, element visual, element visual's translatable
+typedef std::tuple<std::u16string_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_ARALOGX, RID_SINX_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_ARASINX, RID_SINX_HELP, {}, {}},
+ {RID_ARACOSX, RID_COSX_HELP, {}, {}},
+ {RID_ARATANX, RID_TANX_HELP, {}, {}},
+ {RID_ARACOTX, RID_COTX_HELP, {}, {}},
+ {RID_ARASECX, RID_COTX_HELP, {}, {}},
+ {RID_ARACSCX, RID_COTX_HELP, {}, {}},
+ {RID_ARASINHX, RID_SINHX_HELP, {}, {}},
+ {RID_ARACOSHX, RID_COSHX_HELP, {}, {}},
+ {RID_ARATANHX, RID_TANHX_HELP, {}, {}},
+ {RID_ARACOTHX, RID_COTHX_HELP, {}, {}},
+ {RID_ARASECHX, RID_COTX_HELP, {}, {}},
+ {RID_ARACSCHX, RID_COTX_HELP, {}, {}},
+ {},
+ {RID_ARASIN2X, RID_SINX_HELP, {}, {}},
+ {RID_ARACOS2X, RID_COSX_HELP, {}, {}},
+ {RID_ARATAN2X, RID_TANX_HELP, {}, {}},
+ {RID_ARACOT2X, RID_COTX_HELP, {}, {}},
+ {RID_ARASEC2X, RID_COTX_HELP, {}, {}},
+ {RID_ARACSC2X, RID_COTX_HELP, {}, {}},
+ {RID_ARASINH2X, RID_SINHX_HELP, {}, {}},
+ {RID_ARACOSH2X, RID_COSHX_HELP, {}, {}},
+ {RID_ARATANH2X, RID_TANHX_HELP, {}, {}},
+ {RID_ARACOTH2X, RID_COTHX_HELP, {}, {}},
+ {RID_ARASECH2X, RID_COTX_HELP, {}, {}},
+ {RID_ARACSCH2X, RID_COTX_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_HADDX, RID_HADDX_HELP, {}, {}},
+ {RID_HADD_FROMX, RID_HADD_FROMX_HELP, {}, {}},
+ {RID_HADD_TOX, RID_HADD_TOX_HELP, {}, {}},
+ {RID_HADD_FROMTOX, RID_HADD_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_MAJX, RID_MAJX_HELP, {}, {}},
+ {RID_MAJ_FROMX, RID_MAJ_FROMX_HELP, {}, {}},
+ {RID_MAJ_TOX, RID_MAJ_TOX_HELP, {}, {}},
+ {RID_MAJ_FROMTOX, RID_MAJ_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[] =
+{
+ {u"{func e}^{i %pi} + 1 = 0", RID_EXAMPLE_EULER_IDENTITY_HELP, {}, {}},
+ {u"C = %pi cdot d = 2 cdot %pi cdot r", RID_EXAMPLE_CIRCUMFERENCE_HELP, {}, {}},
+ {u"c = sqrt{ a^2 + b^2 }", RID_EXAMPLE_PYTHAGOREAN_THEO_HELP, {}, {}},
+ {u"vec F = m times vec a", RID_EXAMPLE_2NEWTON, {}, {}},
+ {u"E = m c^2", RID_EXAMPLE_MASS_ENERGY_EQUIV_HELP, {}, {}},
+ {u"G_{%mu %nu} + %LAMBDA g_{%mu %nu}= frac{8 %pi G}{c^4} T_{%mu %nu}", RID_EXAMPLE_GENERAL_RELATIVITY_HELP, {}, {}},
+ {u"%DELTA t' = { %DELTA t } over sqrt{ 1 - v^2 over c^2 }", RID_EXAMPLE_SPECIAL_RELATIVITY_HELP, {}, {}},
+ {u"d over dt left( {partial L}over{partial dot q} right) = {partial L}over{partial q}", RID_EXAMPLE_EULER_LAGRANGE_HELP, {}, {}},
+ {u"int from a to b f'(x) dx = f(b) - f(a)", RID_EXAMPLE_FTC_HELP, {}, {}},
+ {u"ldline %delta bold{r}(t) rdline approx e^{%lambda t} ldline %delta { bold{r} }_0 rdline", RID_EXAMPLE_CHAOS_HELP, {}, {}},
+ {u"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, {}, {}},
+ {u"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 };
+}
+
+const std::vector<std::pair<const SmElementDescr*, size_t>> s_a5CategoryDescriptions{
+ { asPair(s_a5UnaryBinaryOperatorsList) },
+ { asPair(s_a5RelationsList) },
+ { asPair(s_a5SetOperationsList) },
+ { asPair(s_a5FunctionsList) },
+ { asPair(s_a5OperatorsList) },
+ { asPair(s_a5AttributesList) },
+ { asPair(s_a5BracketsList) },
+ { asPair(s_a5FormatsList) },
+ { asPair(s_a5OthersList) },
+ { asPair(s_a5ExamplesList) },
+};
+
+} // namespace
+
+// static
+const std::vector<TranslateId>& SmElementsControl::categories()
+{
+ return s_a5Categories;
+}
+
+struct ElementData
+{
+ OUString maElementSource;
+ OUString maHelpText;
+ ElementData(const OUString& aElementSource, const OUString& aHelpText)
+ : maElementSource(aElementSource)
+ , maHelpText(aHelpText)
+ {
+ }
+};
+
+SmElementsControl::SmElementsControl(std::unique_ptr<weld::IconView> pIconView)
+ : mpDocShell(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT))
+ , mnCurrentSetIndex(-1)
+ , m_nSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion())
+ , 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();
+}
+
+Color SmElementsControl::GetTextColor()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return rStyleSettings.GetFieldTextColor();
+}
+
+Color SmElementsControl::GetControlBackground()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return rStyleSettings.GetFieldColor();
+}
+
+namespace
+{
+ // SmTmpDevice::GetTextColor assumes a fg/bg of svtools::FONTCOLOR/svtools::DOCCOLOR
+ // here replace COL_AUTO with the desired fg color, alternatively could add something
+ // to SmTmpDevice to override its defaults
+ class AutoColorVisitor : public SmDefaultingVisitor
+ {
+ private:
+ Color m_aAutoColor;
+ public:
+ AutoColorVisitor(SmNode* pNode, Color aAutoColor)
+ : m_aAutoColor(aAutoColor)
+ {
+ DefaultVisit(pNode);
+ }
+ virtual void DefaultVisit(SmNode* pNode) override
+ {
+ if (pNode->GetFont().GetColor() == COL_AUTO)
+ pNode->GetFont().SetColor(m_aAutoColor);
+ size_t nNodes = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nNodes; ++i)
+ {
+ SmNode* pChild = pNode->GetSubNode(i);
+ if (!pChild)
+ continue;
+ DefaultVisit(pChild);
+ }
+ }
+ };
+}
+
+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->SetMapMode(MapMode(SmMapUnit()));
+ pDevice->SetDrawMode(DrawModeFlags::Default);
+ pDevice->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
+ pDevice->SetDigitLanguage(LANGUAGE_ENGLISH);
+ pDevice->EnableRTL(false);
+
+ pDevice->SetBackground(GetControlBackground());
+ pDevice->SetTextColor(GetTextColor());
+
+ pNode->Prepare(maFormat, *mpDocShell, 0);
+ pNode->SetSize(Fraction(10,8));
+ pNode->Arrange(*pDevice, maFormat);
+
+ AutoColorVisitor(pNode.get(), GetTextColor());
+
+ 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(), maFormat);
+
+ 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::setElementSetIndex(int nSetIndex)
+{
+ if (mnCurrentSetIndex == nSetIndex)
+ return;
+ mnCurrentSetIndex = nSetIndex;
+ build();
+}
+
+void SmElementsControl::addElements(int nCategory)
+{
+ mpIconView->freeze();
+ mpIconView->clear();
+ mpIconView->set_item_width(0);
+ maItemDatas.clear();
+
+ assert(nCategory >= 0 && o3tl::make_unsigned(nCategory) < s_a5CategoryDescriptions.size());
+
+ const auto& [aElementsArray, aElementsArraySize] = s_a5CategoryDescriptions[nCategory];
+
+ 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(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(mnCurrentSetIndex);
+ break;
+ case 6:
+ default:
+ throw std::range_error("parser version limit");
+ }
+}
+
+void SmElementsControl::setSmSyntaxVersion(sal_Int16 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;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/SmElementsPanel.cxx b/starmath/source/SmElementsPanel.cxx
new file mode 100644
index 0000000000..3abc025555
--- /dev/null
+++ b/starmath/source/SmElementsPanel.cxx
@@ -0,0 +1,102 @@
+/* -*- 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 <comphelper/lok.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
+#include <svl/stritem.hxx>
+#include <svl/itemset.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_combo_box("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, -1);
+
+ mxCategoryList->connect_changed(LINK(this, SmElementsPanel, CategorySelectedHandle));
+ mxCategoryList->set_active(0);
+
+ mxElementsControl->setElementSetIndex(0);
+ mxElementsControl->SetSelectHdl(LINK(this, SmElementsPanel, ElementClickHandler));
+}
+
+SmElementsPanel::~SmElementsPanel()
+{
+ mxElementsControl.reset();
+ mxCategoryList.reset();
+}
+
+IMPL_LINK(SmElementsPanel, CategorySelectedHandle, weld::ComboBox&, rList, void)
+{
+ const int nActive = rList.get_active();
+ if (nActive == -1)
+ return;
+ mxElementsControl->setElementSetIndex(nActive);
+ if (SmViewShell* pViewSh = GetView())
+ mxElementsControl->setSmSyntaxVersion(pViewSh->GetDoc()->GetSmSyntaxVersion());
+}
+
+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();
+ SmViewShell* pSmViewShell = dynamic_cast<SmViewShell*>(pView);
+ if (!pSmViewShell && comphelper::LibreOfficeKit::isActive())
+ {
+ auto* pWindow = static_cast<SmGraphicWindow*>(LokStarMathHelper(pView).GetGraphicWindow());
+ if (pWindow)
+ pSmViewShell = &pWindow->GetGraphicWidget().GetView();
+ }
+ return pSmViewShell;
+}
+
+} // 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 0000000000..d7e4609a42
--- /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::ComboBox&, void);
+ DECL_LINK(ElementClickHandler, OUString, void);
+
+ SmViewShell* GetView() const;
+
+ const SfxBindings& mrBindings;
+
+ std::unique_ptr<weld::ComboBox> 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 0000000000..df35dcadff
--- /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, xFrame);
+ }
+ 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 0000000000..48f2c6897c
--- /dev/null
+++ b/starmath/source/SmPropertiesPanel.cxx
@@ -0,0 +1,92 @@
+/* -*- 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,
+ const css::uno::Reference<css::frame::XFrame>& xFrame)
+{
+ return std::make_unique<SmPropertiesPanel>(rParent, xFrame);
+}
+
+SmPropertiesPanel::SmPropertiesPanel(weld::Widget& rParent,
+ const css::uno::Reference<css::frame::XFrame>& xFrame)
+ : PanelLayout(&rParent, "MathPropertiesPanel", "modules/smath/ui/sidebarproperties_math.ui")
+ , mxFrame(xFrame)
+ , 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, mxFrame, {});
+}
+
+} // 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 0000000000..f19316e3fa
--- /dev/null
+++ b/starmath/source/SmPropertiesPanel.hxx
@@ -0,0 +1,54 @@
+/* -*- 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, const css::uno::Reference<css::frame::XFrame>& xFrame);
+ SmPropertiesPanel(weld::Widget& rParent, const css::uno::Reference<css::frame::XFrame>& xFrame);
+ ~SmPropertiesPanel();
+
+private:
+ DECL_LINK(ButtonClickHandler, weld::Button&, void);
+
+ css::uno::Reference<css::frame::XFrame> mxFrame;
+
+ 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 0000000000..055cd0f66e
--- /dev/null
+++ b/starmath/source/accessibility.cxx
@@ -0,0 +1,745 @@
+/* -*- 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 <comphelper/accessibleeventnotifier.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/kernarray.hxx>
+#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_Int64 SAL_CALL SmGraphicAccessible::getAccessibleChildCount()
+{
+ return 0;
+}
+
+Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleChild(
+ sal_Int64 /*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_Int64 SAL_CALL SmGraphicAccessible::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+
+ // -1 for child not found/no parent (according to specification)
+ sal_Int64 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_Int64 nChildCount = xParentContext->getAccessibleChildCount();
+ for (sal_Int64 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
+}
+
+sal_Int64 SAL_CALL SmGraphicAccessible::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = 0;
+
+ if (!pWin)
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ if (pWin->HasFocus())
+ nStateSet |= AccessibleStateType::FOCUSED;
+ if (pWin->IsActive())
+ nStateSet |= AccessibleStateType::ACTIVE;
+ if (pWin->IsVisible())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (pWin->IsReallyVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea();
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ if (COL_TRANSPARENT != rDevice.GetBackground().GetColor())
+ nStateSet |= AccessibleStateType::OPAQUE;
+ }
+
+ return nStateSet;
+}
+
+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();
+
+ KernArray 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();
+
+ KernArray 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()) )
+ {
+ auto nIndexEnd = nIndex;
+ aTxt.iterateCodePoints(&nIndexEnd);
+
+ aResult.SegmentText = aTxt.copy(nIndex, nIndexEnd - nIndex);
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndexEnd;
+ }
+ 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 > 0 )
+ {
+ aTxt.iterateCodePoints(&nIndex, -1);
+ auto nIndexEnd = nIndex;
+ aTxt.iterateCodePoints(&nIndexEnd);
+ aResult.SegmentText = aTxt.copy(nIndex, nIndexEnd - nIndex);
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndexEnd;
+ }
+ 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;
+
+ if ( (AccessibleTextType::CHARACTER == aTextType) && (nIndex + 1 < aTxt.getLength()) )
+ {
+ aTxt.iterateCodePoints(&nIndex);
+ auto nIndexEnd = nIndex;
+ aTxt.iterateCodePoints(&nIndexEnd);
+ aResult.SegmentText = aTxt.copy(nIndex, nIndexEnd - nIndex);
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndexEnd;
+ }
+ 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 0000000000..fd41e5a321
--- /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_Int64 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int64 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 sal_Int64 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 0000000000..37e643c85c
--- /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 0000000000..4e2effaa23
--- /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 0000000000..62918828fd
--- /dev/null
+++ b/starmath/source/cfgitem.cxx
@@ -0,0 +1,1474 @@
+/* -*- 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 <utility>
+#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 OUString SYMBOL_LIST = u"SymbolList"_ustr;
+constexpr OUString FONT_FORMAT_LIST = u"FontFormatList"_ustr;
+
+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/InlineEditEnable",
+ "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/RightToLeft",
+ "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_Int16 nSmSyntaxVersion;
+ bool bPrintTitle;
+ bool bPrintFormulaText;
+ bool bPrintFrame;
+ bool bIsSaveOnlyUsedSymbols;
+ bool bIsAutoCloseBrackets;
+ bool bInlineEditEnable;
+ bool bIgnoreSpacesRight;
+ bool bToolboxVisible;
+ bool bAutoRedraw;
+ bool bFormulaCursor;
+
+ SmCfgOther();
+};
+
+constexpr sal_Int16 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)
+ , bInlineEditEnable(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( OUString _aId, SmFontFormat _aFntFmt ) :
+ aId (std::move(_aId)),
+ aFntFmt (std::move(_aFntFmt))
+{
+}
+
+
+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();
+ }
+ ++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].GetUiName().isEmpty(), "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/InlineEditEnable
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bInlineEditEnable = bTmp;
+ ++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/InlineEditEnable
+ *pVal++ <<= pOther->bInlineEditEnable;
+ // 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/RightToLeft
+ if (pVal->hasValue() && (*pVal >>= bTmp))
+ pFormat->SetRightToLeft( 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, o3tl::convert(nTmp16, o3tl::Length::pt, SmO3tlLengthUnit())));
+ ++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/RightToLeft
+ *pValue++ <<= pFormat->IsRightToLeft();
+ // 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>(
+ o3tl::convert(pFormat->GetBaseSize().Height(), SmO3tlLengthUnit(), o3tl::Length::pt));
+
+ 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_Int16 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_Int16 nVal )
+{
+ if (!pOther)
+ LoadOther();
+ if (nVal != pOther->nSmSyntaxVersion)
+ {
+ CommitLocker aLock(*this);
+ pOther->nSmSyntaxVersion = nVal;
+ SetOtherModified( true );
+ }
+}
+
+bool SmMathConfig::IsInlineEditEnable() const
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return false;
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bInlineEditEnable;
+}
+
+
+void SmMathConfig::SetInlineEditEnable( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ if (SetOtherIfNotEqual( pOther->bInlineEditEnable, bVal ))
+ {
+ // reformat (displayed) formulas accordingly
+ Broadcast(SfxHint(SfxHintId::MathFormatChanged));
+ }
+}
+
+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_INLINE_EDIT_ENABLE))
+ { bVal = pSpacesItem->GetValue();
+ SetInlineEditEnable( 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_INLINE_EDIT_ENABLE, IsInlineEditEnable()));
+ 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 0000000000..6f8e9b0af0
--- /dev/null
+++ b/starmath/source/cursor.cxx
@@ -0,0 +1,1606 @@
+/* -*- 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 <comphelper/lok.hxx>
+#include <editeng/editeng.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <osl/diagnose.h>
+#include <sfx2/lokhelper.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/unohelp2.hxx>
+
+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() const {
+ //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);
+}
+
+tools::Rectangle SmCursor::GetCaretRectangle(OutputDevice& rOutDev) const
+{
+ return SmCaretRectanglesVisitor(rOutDev, GetPosition()).getCaret();
+}
+
+tools::Rectangle SmCursor::GetSelectionRectangle(OutputDevice& rOutDev) const
+{
+ AnnotateSelection();
+ return SmSelectionRectanglesVisitor(rOutDev, mpTree).GetSelection();
+}
+
+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);
+ const bool bSelectedIsTopMost = pLine == 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); // deletes pLine, potentially deleting pos.pSelectedNode
+
+ //Find iterator for place to insert nodes
+ SmNodeList::iterator it = bSelectedIsTopMost ? pLineList->begin()
+ : 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""_ustr;
+ 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""_ustr;
+ 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(vcl::Window* pWindow)
+{
+ 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
+ // TODO: Simplify all this cloning since we only need a formula string now.
+ 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)));
+ }
+ }
+
+ // Parse list of nodes to a tree
+ SmNodeListParser parser;
+ SmNode* pTree(parser.Parse(CloneList(aClipboard).get()));
+
+ // Parse the tree to a string
+ OUString aString;
+ SmNodeToTextVisitor(pTree, aString);
+
+ //Set clipboard
+ auto xClipboard(pWindow ? pWindow->GetClipboard() : GetSystemClipboard());
+ vcl::unohelper::TextDataObject::CopyStringTo(aString, xClipboard);
+}
+
+void SmCursor::Paste(vcl::Window* pWindow)
+{
+ BeginEdit();
+ Delete();
+
+ auto xClipboard(pWindow ? pWindow->GetClipboard() : GetSystemClipboard());
+ auto aDataHelper(TransferableDataHelper::CreateFromClipboard(xClipboard));
+ if (aDataHelper.GetTransferable().is())
+ {
+ // TODO: Support MATHML
+ auto nId = SotClipboardFormatId::STRING;
+ if (aDataHelper.HasFormat(nId))
+ {
+ OUString aString;
+ if (aDataHelper.GetString(nId, aString))
+ InsertCommandText(aString);
+ }
+ }
+
+ 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 (comphelper::LibreOfficeKit::isActive())
+ {
+ pViewSh->SendCaretToLOK();
+ }
+ else 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 0000000000..1ad8376e5a
--- /dev/null
+++ b/starmath/source/dialog.cxx
@@ -0,0 +1,2170 @@
+/* -*- 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 <officecfg/Office/Math.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 = rStyleSettings.GetFaceColor();
+ rTextColor = rStyleSettings.GetLabelTextColor();
+ }
+}
+
+// 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;
+};
+
+vcl::Font lclGetSymbolFont(const SmViewShell& rViewShell, const SmSym &rSymbol)
+{
+ const SmDocShell* pDoc = rViewShell.GetDoc();
+ if (pDoc)
+ {
+ // If we have a document, we want to render the symbol using the font and style used in
+ // the document, so we do that by creating a node and preparing it, then get the resolved
+ // font and style from it.
+ SmToken token(TSPECIAL, '\0', "%" + rSymbol.GetUiName());
+ SmSpecialNode aNode(token);
+ aNode.Prepare(pDoc->GetFormat(), *pDoc, 1);
+ aNode.PrepareAttributes();
+ return aNode.GetFont();
+ }
+
+ return rSymbol.GetFace();
+}
+
+} // 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_xTitleImg(m_xBuilder->weld_widget("locktitle"))
+ , m_xText(m_xBuilder->weld_check_button("text"))
+ , m_xTextImg(m_xBuilder->weld_widget("locktext"))
+ , m_xFrame(m_xBuilder->weld_check_button("frame"))
+ , m_xFrameImg(m_xBuilder->weld_widget("lockframe"))
+ , 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_xLockPrintImg(m_xBuilder->weld_widget("lockprintformat"))
+ , m_xZoom(m_xBuilder->weld_metric_spin_button("zoom", FieldUnit::PERCENT))
+ , m_xEnableInlineEdit(m_xBuilder->weld_check_button("enableinlineedit"))
+ , m_xEnableInlineEditImg(m_xBuilder->weld_widget("lockenableinlineedit"))
+ , m_xNoRightSpaces(m_xBuilder->weld_check_button("norightspaces"))
+ , m_xNoRightSpacesImg(m_xBuilder->weld_widget("locknorightspaces"))
+ , m_xSaveOnlyUsedSymbols(m_xBuilder->weld_check_button("saveonlyusedsymbols"))
+ , m_xSaveOnlyUsedSymbolsImg(m_xBuilder->weld_widget("locksaveonlyusedsymbols"))
+ , m_xAutoCloseBrackets(m_xBuilder->weld_check_button("autoclosebrackets"))
+ , m_xAutoCloseBracketsImg(m_xBuilder->weld_widget("lockautoclosebrackets"))
+ , m_xSmZoom(m_xBuilder->weld_metric_spin_button("smzoom", FieldUnit::PERCENT))
+ , m_xSmZoomImg(m_xBuilder->weld_widget("locksmzoom"))
+{
+ 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())
+ if (SmEditWindow* pEdit = pViewSh->GetEditWindow())
+ pEdit->UpdateStatus();
+}
+
+OUString SmPrintOptionsTabPage::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[] = { "label4", "label5", "label1", "label6" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString checkButton[]
+ = { "title", "text", "frame", "norightspaces", "saveonlyusedsymbols", "autoclosebrackets" };
+
+ for (const auto& check : checkButton)
+ {
+ if (const auto& pString = m_xBuilder->weld_check_button(check))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString radioButton[] = { "sizenormal", "sizescaled", "sizezoomed" };
+
+ for (const auto& radio : radioButton)
+ {
+ if (const auto& pString = m_xBuilder->weld_radio_button(radio))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+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_INLINE_EDIT_ENABLE, m_xEnableInlineEdit->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())
+ if (SmEditWindow* pEdit = pViewSh->GetEditWindow())
+ pEdit->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);
+ bool bReadOnly = officecfg::Office::Math::Print::Size::isReadOnly();
+ if (bReadOnly)
+ {
+ m_xSizeNormal->set_sensitive(false);
+ m_xSizeScaled->set_sensitive(false);
+ m_xSizeZoomed->set_sensitive(false);
+ m_xLockPrintImg->set_visible(true);
+ }
+
+ bReadOnly = officecfg::Office::Math::Print::ZoomFactor::isReadOnly();
+ m_xZoom->set_value(rSet->Get(SID_PRINTZOOM).GetValue(), FieldUnit::PERCENT);
+ m_xZoom->set_sensitive(m_xSizeZoomed->get_active() && !bReadOnly);
+
+ bReadOnly = officecfg::Office::Math::Misc::SmEditWindowZoomFactor::isReadOnly();
+ m_xSmZoom->set_value(rSet->Get(SID_SMEDITWINDOWZOOM).GetValue(), FieldUnit::PERCENT);
+ m_xSmZoom->set_sensitive(!bReadOnly);
+ m_xSmZoomImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Math::Print::Title::isReadOnly();
+ m_xTitle->set_active(rSet->Get(SID_PRINTTITLE).GetValue());
+ m_xTitle->set_sensitive(!bReadOnly);
+ m_xTitleImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Math::Print::FormulaText::isReadOnly();
+ m_xText->set_active(rSet->Get(GetWhich(SID_PRINTTEXT)).GetValue());
+ m_xText->set_sensitive(!bReadOnly);
+ m_xTextImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Math::Print::Frame::isReadOnly();
+ m_xFrame->set_active(rSet->Get(GetWhich(SID_PRINTFRAME)).GetValue());
+ m_xFrame->set_sensitive(!bReadOnly);
+ m_xFrameImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Math::Misc::InlineEditEnable::isReadOnly();
+ m_xEnableInlineEdit->set_active(rSet->Get(SID_INLINE_EDIT_ENABLE).GetValue());
+ m_xEnableInlineEdit->set_sensitive(!bReadOnly);
+ m_xEnableInlineEditImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Math::Misc::IgnoreSpacesRight::isReadOnly();
+ m_xNoRightSpaces->set_active(rSet->Get(SID_NO_RIGHT_SPACES).GetValue());
+ m_xNoRightSpaces->set_sensitive(!bReadOnly);
+ m_xNoRightSpacesImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Math::LoadSave::IsSaveOnlyUsedSymbols::isReadOnly();
+ m_xSaveOnlyUsedSymbols->set_active(rSet->Get(SID_SAVE_ONLY_USED_SYMBOLS).GetValue());
+ m_xSaveOnlyUsedSymbols->set_sensitive(!bReadOnly);
+ m_xSaveOnlyUsedSymbolsImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Math::Misc::AutoCloseBrackets::isReadOnly();
+ m_xAutoCloseBrackets->set_active(rSet->Get(SID_AUTO_CLOSE_BRACKETS).GetValue());
+ m_xAutoCloseBrackets->set_sensitive(!bReadOnly);
+ m_xAutoCloseBracketsImg->set_visible(bReadOnly);
+}
+
+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(
+ o3tl::convert(rFormat.GetBaseSize().Height(), SmO3tlLengthUnit(), o3tl::Length::pt),
+ 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, o3tl::convert(m_xBaseSize->get_value(FieldUnit::NONE), o3tl::Length::pt, SmO3tlLengthUnit())) );
+
+ 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 OUString&, rIdent, void)
+{
+ SmFontPickListBox *pActiveListBox;
+
+ bool bHideCheckboxes = false;
+ if (rIdent == "math")
+ pActiveListBox = m_xMathFont.get();
+ else 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_xMathFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("mathCB")))
+ , 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_xMathFont = pp->GetConfig()->GetFontPickList(FNT_MATH);
+ *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_xMathFont->Insert( rFormat.GetFont(FNT_MATH) );
+ 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_MATH) = *m_xMathFont;
+ 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_MATH, m_xMathFont->Get() );
+ 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(OUString::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(OUString::number(nCategoryIdx)+"label"+OUString::number(i+1)));
+
+ if (xLabel)
+ {
+ Strings[i] = xLabel->get_label();
+ Graphics[i] = rBuilder.weld_widget(OUString::number(nCategoryIdx)+"image"+OUString::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 OUString&, 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 constexpr OUString EMPTY(u""_ustr);
+ static constexpr OUString aCatMf2Hid[10][4] =
+ {
+ { HID_SMA_DEFAULT_DIST, HID_SMA_LINE_DIST, HID_SMA_ROOT_DIST, EMPTY },
+ { HID_SMA_SUP_DIST, HID_SMA_SUB_DIST , EMPTY, EMPTY },
+ { HID_SMA_NUMERATOR_DIST, HID_SMA_DENOMINATOR_DIST, EMPTY, EMPTY },
+ { HID_SMA_FRACLINE_EXCWIDTH, HID_SMA_FRACLINE_LINEWIDTH, EMPTY, EMPTY },
+ { HID_SMA_UPPERLIMIT_DIST, HID_SMA_LOWERLIMIT_DIST, EMPTY, EMPTY },
+ { HID_SMA_BRACKET_EXCHEIGHT, HID_SMA_BRACKET_DIST, EMPTY, HID_SMA_BRACKET_EXCHEIGHT2 },
+ { HID_SMA_MATRIXROW_DIST, HID_SMA_MATRIXCOL_DIST, EMPTY, EMPTY },
+ { HID_SMA_ATTRIBUT_DIST, HID_SMA_INTERATTRIBUT_DIST, EMPTY, EMPTY },
+ { HID_SMA_OPERATOR_EXCHEIGHT, HID_SMA_OPERATOR_DIST, EMPTY, EMPTY },
+ { 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" + OUString::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].isEmpty();
+
+ 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" + OUString::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, SmViewShell &rViewShell)
+ : m_rViewShell(rViewShell)
+ , 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(lclGetSymbolFont(m_rViewShell, aSymbol));
+ 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(SmViewShell& rViewShell)
+ : m_rViewShell(rViewShell)
+{
+}
+
+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(lclGetSymbolFont(m_rViewShell, *pSymbol));
+ 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->GetUiName() + " ";
+
+ 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_aSymbolDisplay(rViewShell)
+ , m_xSymbolSets(m_xBuilder->weld_combo_box("symbolset"))
+ , m_xSymbolSetDisplay(new SmShowSymbolSet(m_xBuilder->weld_scrolled_window("scrolledwindow", true), rViewShell))
+ , 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->GetUiName() : 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->GetUiName());
+}
+
+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.GetSymbolByUiName(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.GetSymbolByUiName(aTmpSymbolName) == NULL, "symbol already exists" );
+ m_aSymbolMgrCopy.AddOrReplaceSymbol( aNewSymbol );
+
+ // update display of new symbol
+ m_aSymbolDisplay.SetSymbol( &aNewSymbol );
+ m_xSymbolName->set_label(aNewSymbol.GetUiName());
+ 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.GetUiName());
+ 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->GetUiName());
+
+ // 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->GetUiName()
+ && 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.GetSymbolByUiName(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->GetUiName();
+ 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->GetUiName());
+ }
+ }
+
+ 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.GetSymbolByUiName(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 0000000000..ff28f448cb
--- /dev/null
+++ b/starmath/source/document.cxx
@@ -0,0 +1,1583 @@
+/* -*- 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 <comphelper/string.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 <comphelper/diagnose_ex.hxx>
+#include <visitors.hxx>
+#include "accessibility.hxx"
+#include <cfgitem.hxx>
+#include <utility>
+#include <oox/mathml/imexport.hxx>
+#include <ElementsDockingWindow.hxx>
+#include <smediteng.hxx>
+#include <editeng/editund2.hxx>
+
+#define ShellClass_SmDocShell
+#include <smslots.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SmDocShell, SfxObjectShell)
+
+void SmDocShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("view");
+}
+
+void SmDocShell::SetSmSyntaxVersion(sal_Int16 nSmSyntaxVersion)
+{
+ mnSmSyntaxVersion = nSmSyntaxVersion;
+ maParser.reset(starmathdatabase::GetVersionSmParser(mnSmSyntaxVersion));
+}
+
+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(SmMapUnit()) );
+ }
+ }
+ OSL_ENSURE(pOutDev->GetMapMode().GetMapUnit() == SmMapUnit(),
+ "Sm : wrong MapMode");
+
+ const SmFormat &rFormat = GetFormat();
+ mpTree->Prepare(rFormat, *this, 0);
+
+ pOutDev->Push(vcl::PushFlags::TEXTLAYOUTMODE | vcl::PushFlags::TEXTLANGUAGE |
+ vcl::PushFlags::RTLENABLED);
+
+ // We want the device to always be LTR, we handle RTL formulas ourselves.
+ pOutDev->EnableRTL(false);
+
+ // For RTL formulas, we want the brackets to be mirrored.
+ bool bRTL = GetFormat().IsRightToLeft();
+ pOutDev->SetLayoutMode(bRTL ? vcl::text::ComplexTextLayoutFlags::BiDiRtl
+ : vcl::text::ComplexTextLayoutFlags::Default);
+
+ // Numbers should not be converted, for now.
+ pOutDev->SetDigitLanguage( LANGUAGE_ENGLISH );
+
+ mpTree->Arrange(*pOutDev, rFormat);
+
+ pOutDev->Pop();
+
+ 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();
+
+ bool bRTL = GetFormat().IsRightToLeft();
+
+ // 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 ) );
+
+ Point aPosition(rPosition);
+ if (bRTL && rDev.GetOutDevType() != OUTDEV_WINDOW)
+ aPosition.AdjustX(GetSize().Width()
+ - maFormat.GetDistance(DIS_LEFTSPACE)
+ - maFormat.GetDistance(DIS_RIGHTSPACE));
+
+ //! 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;
+ }
+
+ rDev.Push(vcl::PushFlags::TEXTLAYOUTMODE | vcl::PushFlags::TEXTLANGUAGE |
+ vcl::PushFlags::RTLENABLED);
+
+ // We want the device to always be LTR, we handle RTL formulas ourselves.
+ if (rDev.GetOutDevType() == OUTDEV_WINDOW)
+ rDev.EnableRTL(bRTL);
+ else
+ rDev.EnableRTL(false);
+
+ auto nLayoutFlags = vcl::text::ComplexTextLayoutFlags::Default;
+ if (bRTL)
+ {
+ // For RTL formulas, we want the brackets to be mirrored.
+ nLayoutFlags |= vcl::text::ComplexTextLayoutFlags::BiDiRtl;
+ if (rDev.GetOutDevType() == OUTDEV_WINDOW)
+ nLayoutFlags |= vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
+ }
+
+ rDev.SetLayoutMode(nLayoutFlags);
+
+ // Numbers should not be converted, for now.
+ rDev.SetDigitLanguage( LANGUAGE_ENGLISH );
+
+ //Set selection if any
+ if(mpCursor && bDrawSelection){
+ mpCursor->AnnotateSelection();
+ SmSelectionDrawingVisitor(rDev, mpTree.get(), aPosition);
+ }
+
+ //Drawing using visitor
+ SmDrawingVisitor(rDev, aPosition, mpTree.get(), GetFormat());
+
+ rDev.Pop();
+
+ if (bRestoreDrawMode)
+ rDev.SetDrawMode( nOldDrawMode );
+}
+
+Size SmDocShell::GetSize()
+{
+ Size aRet;
+
+ if (!mpTree)
+ Parse();
+
+ if (mpTree)
+ {
+ ArrangeFormula();
+ aRet = mpTree->GetSize();
+
+ if ( !aRet.Width() || aRet.Width() == 1 )
+ 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 ( SmMapUnit() != eOld )
+ {
+ MapMode aMap( pPrinter->GetMapMode() );
+ aMap.SetMapUnit( SmMapUnit() );
+ Point aTmp( aMap.GetOrigin() );
+ aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, SmMapUnit() ) );
+ aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, SmMapUnit() ) );
+ 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 ( SmMapUnit() != eOld )
+ {
+ MapMode aMap( pRefDev->GetMapMode() );
+ aMap.SetMapUnit( SmMapUnit() );
+ Point aTmp( aMap.GetOrigin() );
+ aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, SmMapUnit() ) );
+ aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, SmMapUnit() ) );
+ 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,
+ SID_INLINE_EDIT_ENABLE, SID_INLINE_EDIT_ENABLE>>(GetPool());
+ SmModule *pp = SM_MOD();
+ pp->GetConfig()->ConfigToItemSet(*pOptions);
+ mpPrinter = VclPtr<SfxPrinter>::Create(std::move(pOptions));
+ mpPrinter->SetMapMode(MapMode(SmMapUnit()));
+ }
+ 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(SmMapUnit()) );
+ 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);
+
+ SetMapUnit(SmMapUnit());
+}
+
+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();
+ }
+ rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(GetModel().get()));
+ 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 ?
+ rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(GetModel().get()));
+ 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::FormulaImExportBase::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 = 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*/,
+ bool /*bOutputForScreen*/)
+{
+ 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 );
+}
+
+void SmDocShell::SetRightToLeft(bool bRTL)
+{
+ SmFormat aOldFormat = GetFormat();
+ if (aOldFormat.IsRightToLeft() == bRTL)
+ return;
+
+ SmFormat aNewFormat(aOldFormat);
+ aNewFormat.SetRightToLeft(bRTL);
+
+ SfxUndoManager* pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat(aNewFormat);
+ Repaint();
+}
+
+static Size 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;
+}
+
+static Size 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;
+}
+
+static void 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);
+}
+
+static void 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 SmDocShell::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, GetTitle(), aOutRect.GetWidth() - 200));
+
+ aFont.SetWeight(WEIGHT_NORMAL);
+ aFont.SetFontSize(aSize600);
+ rOutDev.SetFont(aFont);
+
+ Size aDescSize(GetTextSize(rOutDev, 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, 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, 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, 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, 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(GetSize());
+
+ MapMode OutputMapMode;
+ switch (ePrintSize)
+ {
+ case PRINT_SIZE_NORMAL:
+ OutputMapMode = MapMode(SmMapUnit());
+ break;
+
+ case PRINT_SIZE_SCALED:
+ if (!aSize.IsEmpty())
+ {
+ sal_uInt16 nZ
+ = std::min(o3tl::convert(aOutRect.GetWidth(), 100, aSize.Width()),
+ o3tl::convert(aOutRect.GetHeight(), 100, aSize.Height()));
+ if (bIsPrintFrame && nZ > MINZOOM)
+ nZ -= 10;
+ Fraction aFraction(std::clamp(nZ, MINZOOM, MAXZOOM), 100);
+
+ OutputMapMode = MapMode(SmMapUnit(), Point(), aFraction, aFraction);
+ }
+ else
+ OutputMapMode = MapMode(SmMapUnit());
+ break;
+
+ case PRINT_SIZE_ZOOMED:
+ {
+ Fraction aFraction(nZoomFactor, 100);
+
+ OutputMapMode = MapMode(SmMapUnit(), Point(), aFraction, aFraction);
+ break;
+ }
+ }
+
+ aSize = OutputDevice::LogicToLogic(aSize, OutputMapMode, MapMode(SmMapUnit()));
+
+ Point aPos(aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
+ aOutRect.Top() + (aOutRect.GetHeight() - aSize.Height()) / 2);
+
+ aPos = OutputDevice::LogicToLogic(aPos, MapMode(SmMapUnit()), OutputMapMode);
+ aOutRect = OutputDevice::LogicToLogic(aOutRect, MapMode(SmMapUnit()), OutputMapMode);
+
+ rOutDev.SetMapMode(OutputMapMode);
+ rOutDev.SetClipRegion(vcl::Region(aOutRect));
+ DrawFormula(rOutDev, aPos);
+ rOutDev.SetClipRegion();
+
+ rOutDev.Pop();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/edit.cxx b/starmath/source/edit.cxx
new file mode 100644
index 0000000000..bc1138a18d
--- /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 <svl/itemset.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()
+{
+ GetView()->InvalidateSlots();
+}
+
+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_F1)
+ {
+ mrEditWindow.GetView()->StartMainHelp();
+ return true;
+ }
+
+ 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 (o3tl::trim(selected).empty())
+ autoClose = true;
+ }
+ else
+ {
+ sal_Int32 length = selected.getLength();
+ if (aSelection.nEndPos == length)
+ autoClose = true;
+ else
+ {
+ selected = selected.copy(aSelection.nEndPos);
+ if (o3tl::trim(selected).empty())
+ 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));
+}
+
+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 constexpr 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())
+ {
+ SfxStringItem aTextToFlush(SID_TEXT, GetText());
+ pViewSh->GetViewFrame().GetDispatcher()->ExecuteList(
+ SID_TEXT, SfxCallMode::RECORD,
+ { &aTextToFlush });
+ }
+ }
+ 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 0000000000..f211f1aaa9
--- /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 0000000000..7fd3a476a8
--- /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 0000000000..9440ffb6b1
--- /dev/null
+++ b/starmath/source/format.cxx
@@ -0,0 +1,155 @@
+/* -*- 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, o3tl::convert(12, o3tl::Length::pt, SmO3tlLengthUnit()))
+{
+ eHorAlign = SmHorAlign::Center;
+ nGreekCharStyle = 0;
+ bIsTextmode = bIsRightToLeft = 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());
+ SetRightToLeft(rFormat.IsRightToLeft());
+ 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 &&
+ bIsRightToLeft == rFormat.bIsRightToLeft &&
+ 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 0000000000..ca4a86290d
--- /dev/null
+++ b/starmath/source/mathml/attribute.cxx
@@ -0,0 +1,479 @@
+/* -*- 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""_ustr);
+ 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"_ustr);
+ 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%"_ustr);
+ 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%"_ustr);
+ 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%"_ustr);
+ 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"_ustr);
+ 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 0000000000..484dcd6653
--- /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 0000000000..4f8f2a64ff
--- /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 0000000000..923668f454
--- /dev/null
+++ b/starmath/source/mathml/export.cxx
@@ -0,0 +1,1090 @@
+/* -*- 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 doc shell
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(m_xModel->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& rMediumItemSet = 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 = rMediumItemSet.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"_ustr, 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
+ = rMediumItemSet.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, m_xModel, 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, m_xModel, u"content.xml", xContext, xInfoSet,
+ u"com.sun.star.comp.Math.XMLContentExporter", 5);
+ else
+ bRet = WriteThroughComponentS(xStg, m_xModel, 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, m_xModel, 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, m_xModel, xContext, xInfoSet,
+ u"com.sun.star.comp.Math.XMLContentExporter", 5);
+ else
+ bRet = WriteThroughComponentOS(xOut, m_xModel, 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""_ustr;
+
+ //Get model
+ uno::Reference<lang::XComponent> xModelComp = m_xModel;
+ SAL_WARN_IF(xModelComp == nullptr, "starmath", "Missing model component");
+ SmModel* pModel = m_xModel.get();
+ SAL_WARN_IF(pModel == nullptr, "starmath", "Failed to get threw uno tunnel");
+ if (xModelComp == nullptr || pModel == nullptr)
+ return u""_ustr;
+
+ // Get doc shell
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (pDocShell == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch sm document");
+ return u""_ustr;
+ }
+
+ // 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 = dynamic_cast<SmXMLExport*>(xFilter.get());
+ if (pFilter == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch SmMLExport");
+ return false;
+ }
+ xFilter->filter(aProps);
+ return pFilter->GetSuccess();
+ }
+
+ // filter
+ SmMLExport* pFilter = dynamic_cast<SmMLExport*>(xFilter.get());
+
+ // 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(u"text/xml"_ustr));
+
+ // 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""_ustr;
+
+ // 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
+/*************************************************************************************************/
+
+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(u""_ustr, 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 0000000000..d857e56930
--- /dev/null
+++ b/starmath/source/mathml/import.cxx
@@ -0,0 +1,1405 @@
+/* -*- 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;
+ }
+
+ // Try to get an XStatusIndicator from the Medium
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+
+ // Get model via uno
+ SmModel* pModel = m_xModel.get();
+ 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
+ const SfxUnoAnyItem* pItem = rMedium.GetItemSet().GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem != nullptr)
+ pItem->GetValue() >>= xStatusIndicator;
+ }
+
+ // Create property list
+ static const comphelper::PropertyMapEntry aInfoMap[]
+ = { { u"PrivateData"_ustr, 0, cppu::UnoType<XInterface>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"BaseURI"_ustr, 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"StreamRelPath"_ustr, 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"StreamName"_ustr, 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"_ustr);
+ 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(), m_xModel, u"meta.xml", xContext,
+ xInfoSet,
+ u"com.sun.star.comp.Math.MLOasisMetaImporter", 6);
+ else
+ nWarn
+ = ReadThroughComponentS(rMedium.GetStorage(), m_xModel, 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(), m_xModel, u"settings.xml", xContext,
+ xInfoSet,
+ u"com.sun.star.comp.Math.MLOasisSettingsImporter", 6);
+ else
+ nWarn
+ = ReadThroughComponentS(rMedium.GetStorage(), m_xModel, u"settings.xml", xContext,
+ xInfoSet, u"com.sun.star.comp.Math.XMLSettingsImporter", 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(3);
+
+ // Read document
+ // read a component from storage
+ if (m_pDocShell->GetSmSyntaxVersion() == 5)
+ nWarn = ReadThroughComponentS(rMedium.GetStorage(), m_xModel, u"content.xml", xContext,
+ xInfoSet, u"com.sun.star.comp.Math.XMLImporter", 5);
+ else
+ nWarn = ReadThroughComponentS(rMedium.GetStorage(), m_xModel, 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, m_xModel, xContext, xInfoSet,
+ u"com.sun.star.comp.Math.XMLImporter", false, 5);
+ else
+ nError = ReadThroughComponentIS(xInputStream, m_xModel, 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 = m_xModel.get();
+ 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"_ustr, 0, cppu::UnoType<XInterface>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"BaseURI"_ustr, 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"StreamRelPath"_ustr, 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"StreamName"_ustr, 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 = dynamic_cast<SmXMLImport*>(xFilter.get());
+ if (pXMlImport != nullptr && pXMlImport->GetSuccess())
+ return ERRCODE_NONE;
+ else
+ {
+ SAL_WARN("starmath", "Filter failed on file input");
+ // However this can not be included since it's not public
+ if (pXMlImport == nullptr)
+ return ERRCODE_NONE;
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+ }
+
+ m_pMlImport = dynamic_cast<SmMLImport*>(xFilter.get());
+ 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%"_ustr) };
+ }
+ 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
+/*************************************************************************************************/
+
+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 constexpr OUStringLiteral sFormula(u"Formula");
+ static constexpr OUStringLiteral sBasicLibraries(u"BasicLibraries");
+ static constexpr 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 0000000000..24de35c183
--- /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 0000000000..8ee7536e1c
--- /dev/null
+++ b/starmath/source/mathml/mathmlMo.cxx
@@ -0,0 +1,1128 @@
+/* -*- 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"_ustr, moOpDF::prefix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp },
+ { u"\u2019"_ustr, moOpDF::postfix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp },
+ { u"\u201C"_ustr, moOpDF::prefix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp },
+ { u"\u201D"_ustr, moOpDF::postfix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp },
+ { u"("_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u")"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"["_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"]"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"{"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"|"_ustr, moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"||"_ustr, moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"|||"_ustr, moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"}"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2016"_ustr, moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence },
+ { u"\u2308"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2309"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u230A"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u230B"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2329"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u232A"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2772"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2773"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27E6"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27E7"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27E8"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27E9"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EA"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EB"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EC"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27ED"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EE"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EF"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2980"_ustr, moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence },
+ { u"\u2983"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2984"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2985"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2986"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2987"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2988"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2989"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298A"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298B"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298C"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298D"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298E"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298F"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2990"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2991"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2992"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2993"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2994"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2995"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2996"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2997"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2998"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u29FC"_ustr, moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u29FD"_ustr, moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u";"_ustr, moOpDF::infix, 30, 0, 3, moOpDP::separator | moOpDP::linebreakstyleAfter },
+ { u","_ustr, moOpDF::infix, 40, 0, 3, moOpDP::separator | moOpDP::linebreakstyleAfter },
+ { u"\u2063"_ustr, moOpDF::infix, 40, 0, 0,
+ moOpDP::separator | moOpDP::linebreakstyleAfter },
+ { u"\u2234"_ustr, moOpDF::infix, 70, 5, 5, moOpDP::nonedp },
+ { u"\u2235"_ustr, moOpDF::infix, 70, 5, 5, moOpDP::nonedp },
+ { u"->"_ustr, moOpDF::infix, 90, 5, 5, moOpDP::nonedp },
+ { u".."_ustr, moOpDF::postfix, 100, 0, 0, moOpDP::nonedp },
+ { u"..."_ustr, moOpDF::postfix, 100, 0, 0, moOpDP::nonedp },
+ { u":"_ustr, moOpDF::infix, 100, 1, 2, moOpDP::nonedp },
+ { u"\u03F6"_ustr, moOpDF::infix, 110, 5, 5, moOpDP::nonedp },
+ { u"\u2026"_ustr, moOpDF::infix, 150, 0, 0, moOpDP::nonedp },
+ { u"\u22EE"_ustr, moOpDF::infix, 150, 5, 5, moOpDP::nonedp },
+ { u"\u22EF"_ustr, moOpDF::infix, 150, 0, 0, moOpDP::nonedp },
+ { u"\u22F1"_ustr, moOpDF::infix, 150, 5, 5, moOpDP::nonedp },
+ { u"\u220B"_ustr, moOpDF::infix, 160, 5, 5, moOpDP::nonedp },
+ { u"\u22A2"_ustr, moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22A3"_ustr, moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22A4"_ustr, moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22A8"_ustr, moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22A9"_ustr, moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22AC"_ustr, moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22AD"_ustr, moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22AE"_ustr, moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22AF"_ustr, moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u2228"_ustr, moOpDF::infix, 190, 4, 4, moOpDP::nonedp },
+ { u"&&"_ustr, moOpDF::infix, 200, 4, 4, moOpDP::nonedp },
+ { u"\u2227"_ustr, moOpDF::infix, 200, 4, 4, moOpDP::nonedp },
+ { u"\u2200"_ustr, moOpDF::prefix, 230, 2, 1, moOpDP::nonedp },
+ { u"\u2203"_ustr, moOpDF::prefix, 230, 2, 1, moOpDP::nonedp },
+ { u"\u2204"_ustr, moOpDF::prefix, 230, 2, 1, moOpDP::nonedp },
+ { u"\u2201"_ustr, moOpDF::infix, 240, 1, 2, moOpDP::nonedp },
+ { u"\u2208"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2209"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u220C"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2282"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2282\u20D2"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2283"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2283\u20D2"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2284"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2285"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2286"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2287"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2288"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2289"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u228A"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u228B"_ustr, moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"<="_ustr, moOpDF::infix, 241, 5, 5, moOpDP::nonedp },
+ { u"\u2264"_ustr, moOpDF::infix, 241, 5, 5, moOpDP::nonedp },
+ { u"\u2265"_ustr, moOpDF::infix, 242, 5, 5, moOpDP::nonedp },
+ { u">"_ustr, moOpDF::infix, 243, 5, 5, moOpDP::nonedp },
+ { u">="_ustr, moOpDF::infix, 243, 5, 5, moOpDP::nonedp },
+ { u"\u226F"_ustr, moOpDF::infix, 244, 5, 5, moOpDP::nonedp },
+ { u"&lt"_ustr, moOpDF::infix, 245, 5, 5, moOpDP::nonedp },
+ { u"\u226E"_ustr, moOpDF::infix, 246, 5, 5, moOpDP::nonedp },
+ { u"\u2248"_ustr, moOpDF::infix, 247, 5, 5, moOpDP::nonedp },
+ { u"\u223C"_ustr, moOpDF::infix, 250, 5, 5, moOpDP::nonedp },
+ { u"\u2249"_ustr, moOpDF::infix, 250, 5, 5, moOpDP::nonedp },
+ { u"\u2262"_ustr, moOpDF::infix, 252, 5, 5, moOpDP::nonedp },
+ { u"\u2260"_ustr, moOpDF::infix, 255, 5, 5, moOpDP::nonedp },
+ { u"!="_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"*="_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"+="_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"-="_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"/="_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u":="_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"="_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"=="_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u221D"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2224"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2225"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2226"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2241"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2243"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2244"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2245"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2246"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2247"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u224D"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2254"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2257"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2259"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u225A"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u225B"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u225C"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u225F"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2261"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2268"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2269"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226A"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226A\u0338"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226B"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226B\u0338"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226D"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2270"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2271"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u227A"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u227B"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u227C"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u227D"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2280"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2281"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22A5"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22B4"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22B5"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22C9"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u22CA"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u22CB"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u22CC"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u22D4"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22D6"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22D7"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22D8"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22D9"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22EA"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22EB"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22EC"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22ED"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u25A0"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25A1"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AA"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AB"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AD"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AE"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AF"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25B0"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25B1"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25B3"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B4"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B5"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B6"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B7"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B8"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B9"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25BC"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25BD"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25BE"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25BF"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C0"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C1"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C2"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C3"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C4"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C5"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C6"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C7"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C8"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C9"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25CC"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25CD"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25CE"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25CF"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25D6"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25D7"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25E6"_ustr, moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u29C0"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29C1"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29E3"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29E4"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29E5"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29E6"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29F3"_ustr, moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u2A87"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2A88"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2AAF"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2AAF\u0338"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2AB0"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2AB0\u0338"_ustr, moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2044"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::stretchy },
+ { u"\u2206"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u220A"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u220D"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u220E"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2215"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::stretchy },
+ { u"\u2217"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2218"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2219"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u221F"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2223"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2236"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2237"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2238"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2239"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u223A"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u223B"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u223D"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u223D\u0331"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u223E"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u223F"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2242"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2242\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224A"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224B"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224C"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224E"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224E\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224F"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224F\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2250"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2251"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2252"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2253"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2255"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2256"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2258"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u225D"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u225E"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2263"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2266"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2266\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2267"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u226C"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2272"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2273"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2274"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2275"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2276"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2277"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2278"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2279"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u227E"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u227F"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u227F\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u228C"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u228D"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u228E"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u228F"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u228F\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2290"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2290\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2291"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2292"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2293"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2294"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u229A"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u229B"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u229C"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u229D"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22A6"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22A7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22AA"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22AB"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B6"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B9"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22BA"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22BB"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22BC"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22BD"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22BE"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u22BF"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u22C4"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22C6"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22C7"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22C8"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22CD"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22CE"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22CF"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22D0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22D1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22D2"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22D3"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22D5"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DA"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DB"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DC"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DD"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DE"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DF"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E4"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E5"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E6"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E8"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E9"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F4"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F5"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F6"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F8"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F9"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FA"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FB"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FC"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FD"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FE"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FF"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u25B2"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2758"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2981"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2982"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A0"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A1"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A2"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A3"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A4"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A5"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A6"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A7"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A8"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A9"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AA"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AB"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AC"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AD"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AE"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AF"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B0"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B1"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B2"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B3"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B4"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B5"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B6"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29B7"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29B8"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29B9"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BA"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BB"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BC"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BD"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BE"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BF"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C2"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29C3"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29C4"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C5"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C6"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C7"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C8"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C9"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CA"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CB"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CC"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CD"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CE"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29CF"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29CF\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D0\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D4"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D5"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D6"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29D7"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29D8"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29D9"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29DB"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29DC"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29DD"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29DE"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29E0"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29E1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29E2"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29E7"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29E8"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29E9"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29EA"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29EB"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29EC"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29ED"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29EE"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F0"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F1"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F2"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F5"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29F6"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29F7"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29F8"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F9"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29FA"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29FB"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29FE"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29FF"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A1D"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A1E"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A1F"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A20"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A21"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A22"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A23"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A24"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A25"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A26"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A27"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A28"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A29"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2A"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2B"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2C"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2D"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2E"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A30"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A31"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A32"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A33"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A34"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A35"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A36"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A37"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A38"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A39"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3A"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3B"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3C"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3D"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3E"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A40"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A41"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A42"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A43"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A44"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A45"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A46"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A47"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A48"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A49"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4A"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4B"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4C"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4D"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4E"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4F"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A50"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A51"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A52"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A53"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A54"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A55"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A56"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A57"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A58"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A59"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A5A"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5B"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5C"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5D"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5E"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5F"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A60"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A61"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A62"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A63"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A64"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A65"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A66"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A67"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A68"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A69"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6A"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6B"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6C"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6D"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6E"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6F"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A70"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A71"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A72"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A73"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A74"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A75"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A76"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A77"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A78"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A79"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7A"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7B"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7C"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7D"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7D\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7E"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7E\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7F"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A80"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A81"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A82"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A83"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A84"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A85"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A86"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A89"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8A"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8B"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8C"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8D"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8E"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8F"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A90"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A91"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A92"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A93"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A94"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A95"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A96"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A97"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A98"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A99"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9A"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9B"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9C"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9D"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9E"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9F"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA1\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA2\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA4"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA5"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA6"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA8"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA9"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAA"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAB"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAC"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAD"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAE"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB4"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB5"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB6"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB8"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB9"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABA"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABB"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABC"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABD"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABE"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABF"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC4"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC5"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC6"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC8"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC9"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACA"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACB"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACC"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACD"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACE"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACF"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD4"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD5"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD6"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD8"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD9"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADA"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADB"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADD"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADD\u0338"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADE"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADF"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE4"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE5"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE6"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE8"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE9"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEA"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEB"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEC"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AED"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEE"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEF"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF0"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF1"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF2"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF3"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF4"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AF5"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AF6"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AF7"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF8"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF9"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AFA"_ustr, moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AFB"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AFD"_ustr, moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AFE"_ustr, moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"|"_ustr, moOpDF::infix, 270, 2, 2, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"||"_ustr, moOpDF::infix, 270, 2, 2, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"|||"_ustr, moOpDF::infix, 270, 2, 2, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2190"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2191"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2192"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2193"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2194"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2195"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2196"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2197"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2198"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2199"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u219A"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u219B"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u219C"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u219D"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u219E"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u219F"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A0"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A1"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21A2"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A3"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A4"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A5"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21A6"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A7"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21A8"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21A9"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AA"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AB"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AC"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AD"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AE"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21AF"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B0"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B1"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B2"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B3"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B4"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B5"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B6"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21B7"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21B8"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21B9"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21BA"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21BB"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21BC"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21BD"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21BE"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21BF"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C0"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C1"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C2"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C3"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C4"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C5"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C6"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C7"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C8"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C9"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21CA"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21CB"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21CC"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21CD"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21CE"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21CF"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21D0"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21D1"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D2"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21D3"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D4"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21D5"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D6"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D7"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D8"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D9"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21DA"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21DB"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21DC"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21DD"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21DE"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21DF"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21E0"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E1"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21E2"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E3"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21E4"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E5"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E6"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E7"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21E8"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E9"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EA"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EB"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EC"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21ED"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EE"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EF"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21F0"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21F1"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21F2"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21F3"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21F4"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21F5"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21F6"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21F7"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21F8"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21F9"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21FA"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21FB"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21FC"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21FD"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21FE"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21FF"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u22B8"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u27F0"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u27F1"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u27F5"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27F6"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27F7"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27F8"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27F9"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FA"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FB"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FC"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FD"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FE"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FF"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2900"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2901"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2902"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2903"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2904"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2905"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2906"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2907"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2908"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2909"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u290A"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u290B"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u290C"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u290D"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u290E"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u290F"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2910"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2911"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2912"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2913"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2914"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2915"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2916"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2917"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2918"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2919"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291A"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291B"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291C"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291D"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291E"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291F"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2920"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2921"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2922"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2923"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2924"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2925"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2926"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2927"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2928"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2929"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292A"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292B"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292C"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292D"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292E"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292F"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2930"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2931"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2932"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2933"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2934"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2935"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2936"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2937"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2938"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2939"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u293A"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u293B"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u293C"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u293D"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u293E"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u293F"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2940"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2941"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2942"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2943"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2944"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2945"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2946"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2947"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2948"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2949"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u294A"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u294B"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u294C"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u294D"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u294E"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u294F"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2950"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2951"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2952"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2953"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2954"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2955"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2956"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2957"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2958"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2959"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u295A"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u295B"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u295C"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u295D"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u295E"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u295F"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2960"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2961"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2962"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2963"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2964"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2965"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2966"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2967"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2968"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2969"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296A"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296B"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296C"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296D"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296E"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u296F"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2970"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2971"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2972"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2973"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2974"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2975"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2976"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2977"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2978"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2979"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297A"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297B"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297C"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297D"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297E"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u297F"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2999"_ustr, moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299A"_ustr, moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299B"_ustr, moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299C"_ustr, moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299D"_ustr, moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299E"_ustr, moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299F"_ustr, moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u29DF"_ustr, moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u29EF"_ustr, moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u29F4"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2B45"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2B46"_ustr, moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"+"_ustr, moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"+"_ustr, moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"-"_ustr, moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"-"_ustr, moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"\u00B1"_ustr, moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u00B1"_ustr, moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"\u2212"_ustr, moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u2212"_ustr, moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"\u2213"_ustr, moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u2213"_ustr, moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"\u2214"_ustr, moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u229E"_ustr, moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u229F"_ustr, moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u2211"_ustr, moOpDF::prefix, 290, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A0A"_ustr, moOpDF::prefix, 290, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A0B"_ustr, moOpDF::prefix, 290, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u222C"_ustr, moOpDF::prefix, 300, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u222D"_ustr, moOpDF::prefix, 300, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2295"_ustr, moOpDF::infix, 300, 4, 4, moOpDP::nonedp },
+ { u"\u2296"_ustr, moOpDF::infix, 300, 4, 4, moOpDP::nonedp },
+ { u"\u2298"_ustr, moOpDF::infix, 300, 4, 4, moOpDP::nonedp },
+ { u"\u2A01"_ustr, moOpDF::prefix, 300, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u222B"_ustr, moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u222E"_ustr, moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u222F"_ustr, moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2230"_ustr, moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2231"_ustr, moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2232"_ustr, moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2233"_ustr, moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A0C"_ustr, moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A0D"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A0E"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A0F"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A10"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A11"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A12"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A13"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A14"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A15"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A16"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A17"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A18"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A19"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A1A"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A1B"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A1C"_ustr, moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u22C3"_ustr, moOpDF::prefix, 320, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A03"_ustr, moOpDF::prefix, 320, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A04"_ustr, moOpDF::prefix, 320, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u22C0"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u22C1"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u22C2"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A00"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A02"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A05"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A06"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A07"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A08"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A09"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2AFC"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2AFF"_ustr, moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2240"_ustr, moOpDF::infix, 340, 4, 4, moOpDP::nonedp },
+ { u"\u220F"_ustr, moOpDF::prefix, 350, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2210"_ustr, moOpDF::prefix, 350, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2229"_ustr, moOpDF::infix, 350, 4, 4, moOpDP::nonedp },
+ { u"\u222A"_ustr, moOpDF::infix, 350, 4, 4, moOpDP::nonedp },
+ { u"*"_ustr, moOpDF::infix, 390, 3, 3, moOpDP::nonedp },
+ { u"."_ustr, moOpDF::infix, 390, 3, 3, moOpDP::nonedp },
+ { u"\u00D7"_ustr, moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2022"_ustr, moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2043"_ustr, moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2062"_ustr, moOpDF::infix, 390, 0, 0, moOpDP::nonedp },
+ { u"\u22A0"_ustr, moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u22A1"_ustr, moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u22C5"_ustr, moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2A2F"_ustr, moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2A3F"_ustr, moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u00B7"_ustr, moOpDF::infix, 400, 4, 4, moOpDP::nonedp },
+ { u"\u2297"_ustr, moOpDF::infix, 410, 4, 4, moOpDP::nonedp },
+ { u"%"_ustr, moOpDF::infix, 640, 3, 3, moOpDP::nonedp },
+ { u"\\"_ustr, moOpDF::infix, 650, 0, 0, moOpDP::nonedp },
+ { u"\u2216"_ustr, moOpDF::infix, 650, 4, 4, moOpDP::nonedp },
+ { u"/"_ustr, moOpDF::infix, 660, 1, 1, moOpDP::nonedp },
+ { u"\u00F7"_ustr, moOpDF::infix, 660, 4, 4, moOpDP::nonedp },
+ { u"\u2220"_ustr, moOpDF::prefix, 670, 0, 0, moOpDP::nonedp },
+ { u"\u2221"_ustr, moOpDF::prefix, 670, 0, 0, moOpDP::nonedp },
+ { u"\u2222"_ustr, moOpDF::prefix, 670, 0, 0, moOpDP::nonedp },
+ { u"\u00AC"_ustr, moOpDF::prefix, 680, 2, 1, moOpDP::nonedp },
+ { u"\u2299"_ustr, moOpDF::infix, 710, 4, 4, moOpDP::nonedp },
+ { u"\u2202"_ustr, moOpDF::prefix, 740, 2, 1, moOpDP::nonedp },
+ { u"\u2207"_ustr, moOpDF::prefix, 740, 2, 1, moOpDP::nonedp },
+ { u"**"_ustr, moOpDF::infix, 780, 1, 1, moOpDP::nonedp },
+ { u"<>"_ustr, moOpDF::infix, 780, 1, 1, moOpDP::nonedp },
+ { u"^"_ustr, moOpDF::infix, 780, 1, 1, moOpDP::nonedp },
+ { u"\u2032"_ustr, moOpDF::postfix, 800, 0, 0, moOpDP::nonedp },
+ { u"\u266D"_ustr, moOpDF::postfix, 800, 0, 2, moOpDP::nonedp },
+ { u"\u266E"_ustr, moOpDF::postfix, 800, 0, 2, moOpDP::nonedp },
+ { u"\u266F"_ustr, moOpDF::postfix, 800, 0, 2, moOpDP::nonedp },
+ { u"!"_ustr, moOpDF::postfix, 810, 1, 0, moOpDP::nonedp },
+ { u"!!"_ustr, moOpDF::postfix, 810, 1, 0, moOpDP::nonedp },
+ { u"//"_ustr, moOpDF::infix, 820, 1, 1, moOpDP::nonedp },
+ { u"@"_ustr, moOpDF::infix, 825, 1, 1, moOpDP::nonedp },
+ { u"?"_ustr, moOpDF::infix, 835, 1, 1, moOpDP::nonedp },
+ { u"\u2145"_ustr, moOpDF::prefix, 845, 2, 1, moOpDP::nonedp },
+ { u"\u2146"_ustr, moOpDF::prefix, 845, 2, 0, moOpDP::nonedp },
+ { u"\u221A"_ustr, moOpDF::prefix, 845, 1, 1, moOpDP::stretchy },
+ { u"\u221B"_ustr, moOpDF::prefix, 845, 1, 1, moOpDP::nonedp },
+ { u"\u221C"_ustr, moOpDF::prefix, 845, 1, 1, moOpDP::nonedp },
+ { u"\u2061"_ustr, moOpDF::infix, 850, 0, 0, moOpDP::nonedp },
+ { u"\""_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"&"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::nonedp },
+ { u"\'"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"++"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::nonedp },
+ { u"--"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::nonedp },
+ { u"^"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"_"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"`"_ustr, moOpDF::postfix, 880, 0, 00, moOpDP::accent },
+ { u"~"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u00A8"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00AA"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00AF"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u00B0"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::nonedp },
+ { u"\u00B2"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00B3"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00B4"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00B8"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00B9"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00BA"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02C6"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02C7"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02C9"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02CA"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02CB"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02CD"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02D8"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02D9"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02DA"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02DC"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02DD"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02F7"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u0302"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u0311"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u201A"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u201B"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u201E"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u201F"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2033"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2034"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2035"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2036"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2037"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u203E"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2057"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2064"_ustr, moOpDF::infix, 880, 0, 0, moOpDP::nonedp },
+ { u"\u20DB"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u20DC"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u23B4"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23B5"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23DC"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23DD"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23DE"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23DF"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23E0"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23E1"_ustr, moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"_"_ustr, 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 0000000000..5e54f0d92e
--- /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 0000000000..1c18e716e7
--- /dev/null
+++ b/starmath/source/mathml/mathmlexport.cxx
@@ -0,0 +1,1443 @@
+/* -*- 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 <comphelper/genericpropertyset.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#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_uInt32 cChar) { return 0xE000 <= cChar && cChar <= 0xF8FF; }
+
+sal_uInt32 ConvertMathToMathML(std::u16string_view rText, sal_Int32 nIndex = 0)
+{
+ auto cRes = o3tl::iterateCodePoints(rText, &nIndex);
+ if (IsInPrivateUseArea(cRes))
+ {
+ 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");
+
+ const SfxUnoAnyItem* pItem
+ = rMedium.GetItemSet().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 OUString sUsePrettyPrinting(u"UsePrettyPrinting"_ustr);
+ static constexpr OUString sBaseURI(u"BaseURI"_ustr);
+ static constexpr OUString sStreamRelPath(u"StreamRelPath"_ustr);
+ static constexpr OUString sStreamName(u"StreamName"_ustr);
+
+ // 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;
+ 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 = dynamic_cast<SmXMLExport*>(xFilter.get());
+ 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 constexpr OUStringLiteral sMediaType = u"MediaType";
+ static constexpr OUStringLiteral sTextXml = u"text/xml";
+ xSet->setPropertyValue(sMediaType, Any(OUString(sTextXml)));
+
+ // all streams must be encrypted in encrypted document
+ static constexpr 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)
+{
+}
+
+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*/
+ comphelper::AttributeList& 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)
+ {
+ if (!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);
+ }
+ if (pDocShell->GetFormat().IsRightToLeft())
+ {
+ // If the Math equation is set right-to-left, we attach a dir="rtl"
+ // attribute on the <math> root. We don't do anything if it is set
+ // left-to-right, the default dir="ltr" value will be used.
+ AddAttribute(XML_NAMESPACE_MATH, XML_DIR, XML_RTL);
+ }
+ }
+
+ 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_Int16 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));
+ }
+ auto nArse = ConvertMathToMathML(pTemp->GetText());
+ OSL_ENSURE(nArse != 0xffff, "Non existent symbol");
+ GetDocHandler()->characters(OUString(&nArse, 1));
+}
+
+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, 1)));
+ sStrBuf.append('%');
+ break;
+ case FontSizeType::DIVIDE:
+ ::sax::Converter::convertDouble(sStrBuf,
+ static_cast<double>(Fraction(100, 1) / 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
+ double mytest
+ = o3tl::convert<double>(pFontNode->GetFont().GetFontSize().Height(),
+ SmO3tlLengthUnit(), o3tl::Length::pt);
+
+ if (pFontNode->GetSizeType() == FontSizeType::MINUS)
+ mytest -= static_cast<double>(aFrac);
+ else
+ mytest += static_cast<double>(aFrac);
+
+ 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:
+ {
+ const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
+ if (pTemp->GetText().isEmpty())
+ {
+ // 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 0000000000..7bc3e5b913
--- /dev/null
+++ b/starmath/source/mathml/mathmlimport.cxx
@@ -0,0 +1,2673 @@
+/* -*- 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 <comphelper/diagnose_ex.hxx>
+#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());
+
+ OSL_ENSURE(m_xModel.is(), "XMLReader::Read: got no model");
+
+ // try to get an XStatusIndicator from the Medium
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+
+ bool bEmbedded = false;
+ SmModel* pModel = m_xModel.get();
+
+ SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ if (pDocShell)
+ {
+ OSL_ENSURE(pDocShell->GetMedium() == &rMedium, "different SfxMedium found");
+
+ const SfxUnoAnyItem* pItem = rMedium.GetItemSet().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");
+ 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(), m_xModel, "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(), m_xModel, "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(), m_xModel, "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, m_xModel, 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 = dynamic_cast<SmXMLImport*>(xFilter.get());
+ 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())
+{
+}
+
+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));
+}
+
+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 = dynamic_cast<SmModel*>(xModel.get());
+
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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:
+ OUString cBegin;
+ OUString 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();
+ break;
+ case XML_CLOSE:
+ cEnd = aIter.toString();
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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);
+ SmToken bToken;
+ if (bIsFenced)
+ {
+ if (isPrefix)
+ bToken = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(aToken.cMathChar);
+ else if (isInfix)
+ bToken = SmToken(TMLINE, MS_VERTLINE, "mline", TG::NONE, 0);
+ else if (isPostfix)
+ bToken = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(aToken.cMathChar);
+ else
+ bToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(
+ aToken.cMathChar);
+ }
+ else
+ bToken
+ = starmathdatabase::Identify_SmXMLOperatorContext_Impl(aToken.cMathChar, 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+
+ 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""_ustr;
+
+ 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""_ustr;
+ 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""_ustr;
+ 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 = dynamic_cast<SmModel*>(xModel.get());
+
+ 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 constexpr OUStringLiteral sFormula(u"Formula");
+ static constexpr OUStringLiteral sBasicLibraries(u"BasicLibraries");
+ static constexpr 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 0000000000..6eb6d209c8
--- /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(std::u16string_view rText,
+ bool bIsStretchy, sal_Int32 nIndex)
+{
+ auto cChar = o3tl::iterateCodePoints(rText, &nIndex);
+ 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_MAJ:
+ return SmToken(TSUM, MS_MAJ, "maj", 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(std::u16string_view rText,
+ sal_Int32 nIndex)
+{
+ auto cChar = o3tl::iterateCodePoints(rText, &nIndex);
+ 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(std::u16string_view rText,
+ sal_Int32 nIndex)
+{
+ auto cChar = o3tl::iterateCodePoints(rText, &nIndex);
+ 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(std::u16string_view rText,
+ sal_Int32 nIndex)
+{
+ auto cChar = o3tl::iterateCodePoints(rText, &nIndex);
+ 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_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 0000000000..ccfcf0049e
--- /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"_ustr, u"\u00C6"_ustr },
+ { u"AMP"_ustr, u"\u0026"_ustr },
+ { u"Aacute"_ustr, u"\u00C1"_ustr },
+ { u"Abreve"_ustr, u"\u0102"_ustr },
+ { u"Acirc"_ustr, u"\u00C2"_ustr },
+ { u"Acy"_ustr, u"\u0410"_ustr },
+ { u"Afr"_ustr, u"\U0001D504"_ustr },
+ { u"Agrave"_ustr, u"\u00C0"_ustr },
+ { u"Alpha"_ustr, u"\u0391"_ustr },
+ { u"Amacr"_ustr, u"\u0100"_ustr },
+ { u"And"_ustr, u"\u2A53"_ustr },
+ { u"Aogon"_ustr, u"\u0104"_ustr },
+ { u"Aopf"_ustr, u"\U0001D538"_ustr },
+ { u"ApplyFunction"_ustr, u"\u2061"_ustr },
+ { u"Aring"_ustr, u"\u00C5"_ustr },
+ { u"Ascr"_ustr, u"\U0001D49C"_ustr },
+ { u"Assign"_ustr, u"\u2254"_ustr },
+ { u"Atilde"_ustr, u"\u00C3"_ustr },
+ { u"Auml"_ustr, u"\u00C4"_ustr },
+ { u"Backslash"_ustr, u"\u2216"_ustr },
+ { u"Barv"_ustr, u"\u2AE7"_ustr },
+ { u"Barwed"_ustr, u"\u2306"_ustr },
+ { u"Bcy"_ustr, u"\u0411"_ustr },
+ { u"Because"_ustr, u"\u2235"_ustr },
+ { u"Bernoullis"_ustr, u"\u212C"_ustr },
+ { u"Beta"_ustr, u"\u0392"_ustr },
+ { u"Bfr"_ustr, u"\U0001D505"_ustr },
+ { u"Bopf"_ustr, u"\U0001D539"_ustr },
+ { u"Breve"_ustr, u"\u02D8"_ustr },
+ { u"Bscr"_ustr, u"\u212C"_ustr },
+ { u"Bumpeq"_ustr, u"\u224E"_ustr },
+ { u"CHcy"_ustr, u"\u0427"_ustr },
+ { u"COPY"_ustr, u"\u00A9"_ustr },
+ { u"Cacute"_ustr, u"\u0106"_ustr },
+ { u"Cap"_ustr, u"\u22D2"_ustr },
+ { u"CapitalDifferentialD"_ustr, u"\u2145"_ustr },
+ { u"Cayleys"_ustr, u"\u212D"_ustr },
+ { u"Ccaron"_ustr, u"\u010C"_ustr },
+ { u"Ccedil"_ustr, u"\u00C7"_ustr },
+ { u"Ccirc"_ustr, u"\u0108"_ustr },
+ { u"Cconint"_ustr, u"\u2230"_ustr },
+ { u"Cdot"_ustr, u"\u010A"_ustr },
+ { u"Cedilla"_ustr, u"\u00B8"_ustr },
+ { u"CenterDot"_ustr, u"\u00B7"_ustr },
+ { u"Cfr"_ustr, u"\u212D"_ustr },
+ { u"Chi"_ustr, u"\u03A7"_ustr },
+ { u"CircleDot"_ustr, u"\u2299"_ustr },
+ { u"CircleMinus"_ustr, u"\u2296"_ustr },
+ { u"CirclePlus"_ustr, u"\u2295"_ustr },
+ { u"CircleTimes"_ustr, u"\u2297"_ustr },
+ { u"ClockwiseContourIntegral"_ustr, u"\u2232"_ustr },
+ { u"CloseCurlyDoubleQuote"_ustr, u"\u201D"_ustr },
+ { u"CloseCurlyQuote"_ustr, u"\u2019"_ustr },
+ { u"Colon"_ustr, u"\u2237"_ustr },
+ { u"Colone"_ustr, u"\u2A74"_ustr },
+ { u"Congruent"_ustr, u"\u2261"_ustr },
+ { u"Conint"_ustr, u"\u222F"_ustr },
+ { u"ContourIntegral"_ustr, u"\u222E"_ustr },
+ { u"Copf"_ustr, u"\u2102"_ustr },
+ { u"Coproduct"_ustr, u"\u2210"_ustr },
+ { u"CounterClockwiseContourIntegral"_ustr, u"\u2233"_ustr },
+ { u"Cross"_ustr, u"\u2A2F"_ustr },
+ { u"Cscr"_ustr, u"\U0001D49E"_ustr },
+ { u"Cup"_ustr, u"\u22D3"_ustr },
+ { u"CupCap"_ustr, u"\u224D"_ustr },
+ { u"DD"_ustr, u"\u2145"_ustr },
+ { u"DDotrahd"_ustr, u"\u2911"_ustr },
+ { u"DJcy"_ustr, u"\u0402"_ustr },
+ { u"DScy"_ustr, u"\u0405"_ustr },
+ { u"DZcy"_ustr, u"\u040F"_ustr },
+ { u"Dagger"_ustr, u"\u2021"_ustr },
+ { u"Darr"_ustr, u"\u21A1"_ustr },
+ { u"Dashv"_ustr, u"\u2AE4"_ustr },
+ { u"Dcaron"_ustr, u"\u010E"_ustr },
+ { u"Dcy"_ustr, u"\u0414"_ustr },
+ { u"Del"_ustr, u"\u2207"_ustr },
+ { u"Delta"_ustr, u"\u0394"_ustr },
+ { u"Dfr"_ustr, u"\U0001D507"_ustr },
+ { u"DiacriticalAcute"_ustr, u"\u00B4"_ustr },
+ { u"DiacriticalDot"_ustr, u"\u02D9"_ustr },
+ { u"DiacriticalDoubleAcute"_ustr, u"\u02DD"_ustr },
+ { u"DiacriticalGrave"_ustr, u"\u0060"_ustr },
+ { u"DiacriticalTilde"_ustr, u"\u02DC"_ustr },
+ { u"Diamond"_ustr, u"\u22C4"_ustr },
+ { u"DifferentialD"_ustr, u"\u2146"_ustr },
+ { u"Dopf"_ustr, u"\U0001D53B"_ustr },
+ { u"Dot"_ustr, u"\u00A8"_ustr },
+ { u"DotDot"_ustr, u"\u20DC"_ustr },
+ { u"DotEqual"_ustr, u"\u2250"_ustr },
+ { u"DoubleContourIntegral"_ustr, u"\u222F"_ustr },
+ { u"DoubleDot"_ustr, u"\u00A8"_ustr },
+ { u"DoubleDownArrow"_ustr, u"\u21D3"_ustr },
+ { u"DoubleLeftArrow"_ustr, u"\u21D0"_ustr },
+ { u"DoubleLeftRightArrow"_ustr, u"\u21D4"_ustr },
+ { u"DoubleLeftTee"_ustr, u"\u2AE4"_ustr },
+ { u"DoubleLongLeftArrow"_ustr, u"\u27F8"_ustr },
+ { u"DoubleLongLeftRightArrow"_ustr, u"\u27FA"_ustr },
+ { u"DoubleLongRightArrow"_ustr, u"\u27F9"_ustr },
+ { u"DoubleRightArrow"_ustr, u"\u21D2"_ustr },
+ { u"DoubleRightTee"_ustr, u"\u22A8"_ustr },
+ { u"DoubleUpArrow"_ustr, u"\u21D1"_ustr },
+ { u"DoubleUpDownArrow"_ustr, u"\u21D5"_ustr },
+ { u"DoubleVerticalBar"_ustr, u"\u2225"_ustr },
+ { u"DownArrow"_ustr, u"\u2193"_ustr },
+ { u"DownArrowBar"_ustr, u"\u2913"_ustr },
+ { u"DownArrowUpArrow"_ustr, u"\u21F5"_ustr },
+ { u"DownBreve"_ustr, u"\u0311"_ustr },
+ { u"DownLeftRightVector"_ustr, u"\u2950"_ustr },
+ { u"DownLeftTeeVector"_ustr, u"\u295E"_ustr },
+ { u"DownLeftVector"_ustr, u"\u21BD"_ustr },
+ { u"DownLeftVectorBar"_ustr, u"\u2956"_ustr },
+ { u"DownRightTeeVector"_ustr, u"\u295F"_ustr },
+ { u"DownRightVector"_ustr, u"\u21C1"_ustr },
+ { u"DownRightVectorBar"_ustr, u"\u2957"_ustr },
+ { u"DownTee"_ustr, u"\u22A4"_ustr },
+ { u"DownTeeArrow"_ustr, u"\u21A7"_ustr },
+ { u"Downarrow"_ustr, u"\u21D3"_ustr },
+ { u"Dscr"_ustr, u"\U0001D49F"_ustr },
+ { u"Dstrok"_ustr, u"\u0110"_ustr },
+ { u"ENG"_ustr, u"\u014A"_ustr },
+ { u"ETH"_ustr, u"\u00D0"_ustr },
+ { u"Eacute"_ustr, u"\u00C9"_ustr },
+ { u"Ecaron"_ustr, u"\u011A"_ustr },
+ { u"Ecirc"_ustr, u"\u00CA"_ustr },
+ { u"Ecy"_ustr, u"\u042D"_ustr },
+ { u"Edot"_ustr, u"\u0116"_ustr },
+ { u"Efr"_ustr, u"\U0001D508"_ustr },
+ { u"Egrave"_ustr, u"\u00C8"_ustr },
+ { u"Element"_ustr, u"\u2208"_ustr },
+ { u"Emacr"_ustr, u"\u0112"_ustr },
+ { u"EmptySmallSquare"_ustr, u"\u25FB"_ustr },
+ { u"EmptyVerySmallSquare"_ustr, u"\u25AB"_ustr },
+ { u"Eogon"_ustr, u"\u0118"_ustr },
+ { u"Eopf"_ustr, u"\U0001D53C"_ustr },
+ { u"Epsilon"_ustr, u"\u0395"_ustr },
+ { u"Equal"_ustr, u"\u2A75"_ustr },
+ { u"EqualTilde"_ustr, u"\u2242"_ustr },
+ { u"Equilibrium"_ustr, u"\u21CC"_ustr },
+ { u"Escr"_ustr, u"\u2130"_ustr },
+ { u"Esim"_ustr, u"\u2A73"_ustr },
+ { u"Eta"_ustr, u"\u0397"_ustr },
+ { u"Euml"_ustr, u"\u00CB"_ustr },
+ { u"Exists"_ustr, u"\u2203"_ustr },
+ { u"ExponentialE"_ustr, u"\u2147"_ustr },
+ { u"Fcy"_ustr, u"\u0424"_ustr },
+ { u"Ffr"_ustr, u"\U0001D509"_ustr },
+ { u"FilledSmallSquare"_ustr, u"\u25FC"_ustr },
+ { u"FilledVerySmallSquare"_ustr, u"\u25AA"_ustr },
+ { u"Fopf"_ustr, u"\U0001D53D"_ustr },
+ { u"ForAll"_ustr, u"\u2200"_ustr },
+ { u"Fouriertrf"_ustr, u"\u2131"_ustr },
+ { u"Fscr"_ustr, u"\u2131"_ustr },
+ { u"GJcy"_ustr, u"\u0403"_ustr },
+ { u"GT"_ustr, u"\u003E"_ustr },
+ { u"Gamma"_ustr, u"\u0393"_ustr },
+ { u"Gammad"_ustr, u"\u03DC"_ustr },
+ { u"Gbreve"_ustr, u"\u011E"_ustr },
+ { u"Gcedil"_ustr, u"\u0122"_ustr },
+ { u"Gcirc"_ustr, u"\u011C"_ustr },
+ { u"Gcy"_ustr, u"\u0413"_ustr },
+ { u"Gdot"_ustr, u"\u0120"_ustr },
+ { u"Gfr"_ustr, u"\U0001D50A"_ustr },
+ { u"Gg"_ustr, u"\u22D9"_ustr },
+ { u"Gopf"_ustr, u"\U0001D53E"_ustr },
+ { u"GreaterEqual"_ustr, u"\u2265"_ustr },
+ { u"GreaterEqualLess"_ustr, u"\u22DB"_ustr },
+ { u"GreaterFullEqual"_ustr, u"\u2267"_ustr },
+ { u"GreaterGreater"_ustr, u"\u2AA2"_ustr },
+ { u"GreaterLess"_ustr, u"\u2277"_ustr },
+ { u"GreaterSlantEqual"_ustr, u"\u2A7E"_ustr },
+ { u"GreaterTilde"_ustr, u"\u2273"_ustr },
+ { u"Gscr"_ustr, u"\U0001D4A2"_ustr },
+ { u"Gt"_ustr, u"\u226B"_ustr },
+ { u"HARDcy"_ustr, u"\u042A"_ustr },
+ { u"Hacek"_ustr, u"\u02C7"_ustr },
+ { u"Hat"_ustr, u"\u005E"_ustr },
+ { u"Hcirc"_ustr, u"\u0124"_ustr },
+ { u"Hfr"_ustr, u"\u210C"_ustr },
+ { u"HilbertSpace"_ustr, u"\u210B"_ustr },
+ { u"Hopf"_ustr, u"\u210D"_ustr },
+ { u"HorizontalLine"_ustr, u"\u2500"_ustr },
+ { u"Hscr"_ustr, u"\u210B"_ustr },
+ { u"Hstrok"_ustr, u"\u0126"_ustr },
+ { u"HumpDownHump"_ustr, u"\u224E"_ustr },
+ { u"HumpEqual"_ustr, u"\u224F"_ustr },
+ { u"IEcy"_ustr, u"\u0415"_ustr },
+ { u"IJlig"_ustr, u"\u0132"_ustr },
+ { u"IOcy"_ustr, u"\u0401"_ustr },
+ { u"Iacute"_ustr, u"\u00CD"_ustr },
+ { u"Icirc"_ustr, u"\u00CE"_ustr },
+ { u"Icy"_ustr, u"\u0418"_ustr },
+ { u"Idot"_ustr, u"\u0130"_ustr },
+ { u"Ifr"_ustr, u"\u2111"_ustr },
+ { u"Igrave"_ustr, u"\u00CC"_ustr },
+ { u"Im"_ustr, u"\u2111"_ustr },
+ { u"Imacr"_ustr, u"\u012A"_ustr },
+ { u"ImaginaryI"_ustr, u"\u2148"_ustr },
+ { u"Implies"_ustr, u"\u21D2"_ustr },
+ { u"Int"_ustr, u"\u222C"_ustr },
+ { u"Integral"_ustr, u"\u222B"_ustr },
+ { u"Intersection"_ustr, u"\u22C2"_ustr },
+ { u"InvisibleComma"_ustr, u"\u2063"_ustr },
+ { u"InvisibleTimes"_ustr, u"\u2062"_ustr },
+ { u"Iogon"_ustr, u"\u012E"_ustr },
+ { u"Iopf"_ustr, u"\U0001D540"_ustr },
+ { u"Iota"_ustr, u"\u0399"_ustr },
+ { u"Iscr"_ustr, u"\u2110"_ustr },
+ { u"Itilde"_ustr, u"\u0128"_ustr },
+ { u"Iukcy"_ustr, u"\u0406"_ustr },
+ { u"Iuml"_ustr, u"\u00CF"_ustr },
+ { u"Jcirc"_ustr, u"\u0134"_ustr },
+ { u"Jcy"_ustr, u"\u0419"_ustr },
+ { u"Jfr"_ustr, u"\U0001D50D"_ustr },
+ { u"Jopf"_ustr, u"\U0001D541"_ustr },
+ { u"Jscr"_ustr, u"\U0001D4A5"_ustr },
+ { u"Jsercy"_ustr, u"\u0408"_ustr },
+ { u"Jukcy"_ustr, u"\u0404"_ustr },
+ { u"KHcy"_ustr, u"\u0425"_ustr },
+ { u"KJcy"_ustr, u"\u040C"_ustr },
+ { u"Kappa"_ustr, u"\u039A"_ustr },
+ { u"Kcedil"_ustr, u"\u0136"_ustr },
+ { u"Kcy"_ustr, u"\u041A"_ustr },
+ { u"Kfr"_ustr, u"\U0001D50E"_ustr },
+ { u"Kopf"_ustr, u"\U0001D542"_ustr },
+ { u"Kscr"_ustr, u"\U0001D4A6"_ustr },
+ { u"LJcy"_ustr, u"\u0409"_ustr },
+ { u"LT"_ustr, u"\u003C"_ustr },
+ { u"Lacute"_ustr, u"\u0139"_ustr },
+ { u"Lambda"_ustr, u"\u039B"_ustr },
+ { u"Lang"_ustr, u"\u27EA"_ustr },
+ { u"Laplacetrf"_ustr, u"\u2112"_ustr },
+ { u"Larr"_ustr, u"\u219E"_ustr },
+ { u"Lcaron"_ustr, u"\u013D"_ustr },
+ { u"Lcedil"_ustr, u"\u013B"_ustr },
+ { u"Lcy"_ustr, u"\u041B"_ustr },
+ { u"LeftAngleBracket"_ustr, u"\u27E8"_ustr },
+ { u"LeftArrow"_ustr, u"\u2190"_ustr },
+ { u"LeftArrowBar"_ustr, u"\u21E4"_ustr },
+ { u"LeftArrowRightArrow"_ustr, u"\u21C6"_ustr },
+ { u"LeftCeiling"_ustr, u"\u2308"_ustr },
+ { u"LeftDoubleBracket"_ustr, u"\u27E6"_ustr },
+ { u"LeftDownTeeVector"_ustr, u"\u2961"_ustr },
+ { u"LeftDownVector"_ustr, u"\u21C3"_ustr },
+ { u"LeftDownVectorBar"_ustr, u"\u2959"_ustr },
+ { u"LeftFloor"_ustr, u"\u230A"_ustr },
+ { u"LeftRightArrow"_ustr, u"\u2194"_ustr },
+ { u"LeftRightVector"_ustr, u"\u294E"_ustr },
+ { u"LeftTee"_ustr, u"\u22A3"_ustr },
+ { u"LeftTeeArrow"_ustr, u"\u21A4"_ustr },
+ { u"LeftTeeVector"_ustr, u"\u295A"_ustr },
+ { u"LeftTriangle"_ustr, u"\u22B2"_ustr },
+ { u"LeftTriangleBar"_ustr, u"\u29CF"_ustr },
+ { u"LeftTriangleEqual"_ustr, u"\u22B4"_ustr },
+ { u"LeftUpDownVector"_ustr, u"\u2951"_ustr },
+ { u"LeftUpTeeVector"_ustr, u"\u2960"_ustr },
+ { u"LeftUpVector"_ustr, u"\u21BF"_ustr },
+ { u"LeftUpVectorBar"_ustr, u"\u2958"_ustr },
+ { u"LeftVector"_ustr, u"\u21BC"_ustr },
+ { u"LeftVectorBar"_ustr, u"\u2952"_ustr },
+ { u"Leftarrow"_ustr, u"\u21D0"_ustr },
+ { u"Leftrightarrow"_ustr, u"\u21D4"_ustr },
+ { u"LessEqualGreater"_ustr, u"\u22DA"_ustr },
+ { u"LessFullEqual"_ustr, u"\u2266"_ustr },
+ { u"LessGreater"_ustr, u"\u2276"_ustr },
+ { u"LessLess"_ustr, u"\u2AA1"_ustr },
+ { u"LessSlantEqual"_ustr, u"\u2A7D"_ustr },
+ { u"LessTilde"_ustr, u"\u2272"_ustr },
+ { u"Lfr"_ustr, u"\U0001D50F"_ustr },
+ { u"Ll"_ustr, u"\u22D8"_ustr },
+ { u"Lleftarrow"_ustr, u"\u21DA"_ustr },
+ { u"Lmidot"_ustr, u"\u013F"_ustr },
+ { u"LongLeftArrow"_ustr, u"\u27F5"_ustr },
+ { u"LongLeftRightArrow"_ustr, u"\u27F7"_ustr },
+ { u"LongRightArrow"_ustr, u"\u27F6"_ustr },
+ { u"Longleftarrow"_ustr, u"\u27F8"_ustr },
+ { u"Longleftrightarrow"_ustr, u"\u27FA"_ustr },
+ { u"Longrightarrow"_ustr, u"\u27F9"_ustr },
+ { u"Lopf"_ustr, u"\U0001D543"_ustr },
+ { u"LowerLeftArrow"_ustr, u"\u2199"_ustr },
+ { u"LowerRightArrow"_ustr, u"\u2198"_ustr },
+ { u"Lscr"_ustr, u"\u2112"_ustr },
+ { u"Lsh"_ustr, u"\u21B0"_ustr },
+ { u"Lstrok"_ustr, u"\u0141"_ustr },
+ { u"Lt"_ustr, u"\u226A"_ustr },
+ { u"Map"_ustr, u"\u2905"_ustr },
+ { u"Mcy"_ustr, u"\u041C"_ustr },
+ { u"MediumSpace"_ustr, u"\u205F"_ustr },
+ { u"Mellintrf"_ustr, u"\u2133"_ustr },
+ { u"Mfr"_ustr, u"\U0001D510"_ustr },
+ { u"MinusPlus"_ustr, u"\u2213"_ustr },
+ { u"Mopf"_ustr, u"\U0001D544"_ustr },
+ { u"Mscr"_ustr, u"\u2133"_ustr },
+ { u"Mu"_ustr, u"\u039C"_ustr },
+ { u"NJcy"_ustr, u"\u040A"_ustr },
+ { u"Nacute"_ustr, u"\u0143"_ustr },
+ { u"Ncaron"_ustr, u"\u0147"_ustr },
+ { u"Ncedil"_ustr, u"\u0145"_ustr },
+ { u"Ncy"_ustr, u"\u041D"_ustr },
+ { u"NegativeMediumSpace"_ustr, u"\u200B"_ustr },
+ { u"NegativeThickSpace"_ustr, u"\u200B"_ustr },
+ { u"NegativeThinSpace"_ustr, u"\u200B"_ustr },
+ { u"NegativeVeryThinSpace"_ustr, u"\u200B"_ustr },
+ { u"NestedGreaterGreater"_ustr, u"\u226B"_ustr },
+ { u"NestedLessLess"_ustr, u"\u226A"_ustr },
+ { u"NewLine"_ustr, u"\u000A"_ustr },
+ { u"Nfr"_ustr, u"\U0001D511"_ustr },
+ { u"NoBreak"_ustr, u"\u2060"_ustr },
+ { u"NonBreakingSpace"_ustr, u"\u00A0"_ustr },
+ { u"Nopf"_ustr, u"\u2115"_ustr },
+ { u"Not"_ustr, u"\u2AEC"_ustr },
+ { u"NotCongruent"_ustr, u"\u2262"_ustr },
+ { u"NotCupCap"_ustr, u"\u226D"_ustr },
+ { u"NotDoubleVerticalBar"_ustr, u"\u2226"_ustr },
+ { u"NotElement"_ustr, u"\u2209"_ustr },
+ { u"NotEqual"_ustr, u"\u2260"_ustr },
+ { u"NotEqualTilde"_ustr, u"\u2242\u0338"_ustr },
+ { u"NotExists"_ustr, u"\u2204"_ustr },
+ { u"NotGreater"_ustr, u"\u226F"_ustr },
+ { u"NotGreaterEqual"_ustr, u"\u2271"_ustr },
+ { u"NotGreaterFullEqual"_ustr, u"\u2267\u0338"_ustr },
+ { u"NotGreaterGreater"_ustr, u"\u226B\u0338"_ustr },
+ { u"NotGreaterLess"_ustr, u"\u2279"_ustr },
+ { u"NotGreaterSlantEqual"_ustr, u"\u2A7E\u0338"_ustr },
+ { u"NotGreaterTilde"_ustr, u"\u2275"_ustr },
+ { u"NotHumpDownHump"_ustr, u"\u224E\u0338"_ustr },
+ { u"NotHumpEqual"_ustr, u"\u224F\u0338"_ustr },
+ { u"NotLeftTriangle"_ustr, u"\u22EA"_ustr },
+ { u"NotLeftTriangleBar"_ustr, u"\u29CF\u0338"_ustr },
+ { u"NotLeftTriangleEqual"_ustr, u"\u22EC"_ustr },
+ { u"NotLess"_ustr, u"\u226E"_ustr },
+ { u"NotLessEqual"_ustr, u"\u2270"_ustr },
+ { u"NotLessGreater"_ustr, u"\u2278"_ustr },
+ { u"NotLessLess"_ustr, u"\u226A\u0338"_ustr },
+ { u"NotLessSlantEqual"_ustr, u"\u2A7D\u0338"_ustr },
+ { u"NotLessTilde"_ustr, u"\u2274"_ustr },
+ { u"NotNestedGreaterGreater"_ustr, u"\u2AA2\u0338"_ustr },
+ { u"NotNestedLessLess"_ustr, u"\u2AA1\u0338"_ustr },
+ { u"NotPrecedes"_ustr, u"\u2280"_ustr },
+ { u"NotPrecedesEqual"_ustr, u"\u2AAF\u0338"_ustr },
+ { u"NotPrecedesSlantEqual"_ustr, u"\u22E0"_ustr },
+ { u"NotReverseElement"_ustr, u"\u220C"_ustr },
+ { u"NotRightTriangle"_ustr, u"\u22EB"_ustr },
+ { u"NotRightTriangleBar"_ustr, u"\u29D0\u0338"_ustr },
+ { u"NotRightTriangleEqual"_ustr, u"\u22ED"_ustr },
+ { u"NotSquareSubset"_ustr, u"\u228F\u0338"_ustr },
+ { u"NotSquareSubsetEqual"_ustr, u"\u22E2"_ustr },
+ { u"NotSquareSuperset"_ustr, u"\u2290\u0338"_ustr },
+ { u"NotSquareSupersetEqual"_ustr, u"\u22E3"_ustr },
+ { u"NotSubset"_ustr, u"\u2282\u20D2"_ustr },
+ { u"NotSubsetEqual"_ustr, u"\u2288"_ustr },
+ { u"NotSucceeds"_ustr, u"\u2281"_ustr },
+ { u"NotSucceedsEqual"_ustr, u"\u2AB0\u0338"_ustr },
+ { u"NotSucceedsSlantEqual"_ustr, u"\u22E1"_ustr },
+ { u"NotSucceedsTilde"_ustr, u"\u227F\u0338"_ustr },
+ { u"NotSuperset"_ustr, u"\u2283\u20D2"_ustr },
+ { u"NotSupersetEqual"_ustr, u"\u2289"_ustr },
+ { u"NotTilde"_ustr, u"\u2241"_ustr },
+ { u"NotTildeEqual"_ustr, u"\u2244"_ustr },
+ { u"NotTildeFullEqual"_ustr, u"\u2247"_ustr },
+ { u"NotTildeTilde"_ustr, u"\u2249"_ustr },
+ { u"NotVerticalBar"_ustr, u"\u2224"_ustr },
+ { u"Nscr"_ustr, u"\U0001D4A9"_ustr },
+ { u"Ntilde"_ustr, u"\u00D1"_ustr },
+ { u"Nu"_ustr, u"\u039D"_ustr },
+ { u"OElig"_ustr, u"\u0152"_ustr },
+ { u"Oacute"_ustr, u"\u00D3"_ustr },
+ { u"Ocirc"_ustr, u"\u00D4"_ustr },
+ { u"Ocy"_ustr, u"\u041E"_ustr },
+ { u"Odblac"_ustr, u"\u0150"_ustr },
+ { u"Ofr"_ustr, u"\U0001D512"_ustr },
+ { u"Ograve"_ustr, u"\u00D2"_ustr },
+ { u"Omacr"_ustr, u"\u014C"_ustr },
+ { u"Omega"_ustr, u"\u03A9"_ustr },
+ { u"Omicron"_ustr, u"\u039F"_ustr },
+ { u"Oopf"_ustr, u"\U0001D546"_ustr },
+ { u"OpenCurlyDoubleQuote"_ustr, u"\u201C"_ustr },
+ { u"OpenCurlyQuote"_ustr, u"\u2018"_ustr },
+ { u"Or"_ustr, u"\u2A54"_ustr },
+ { u"Oscr"_ustr, u"\U0001D4AA"_ustr },
+ { u"Oslash"_ustr, u"\u00D8"_ustr },
+ { u"Otilde"_ustr, u"\u00D5"_ustr },
+ { u"Otimes"_ustr, u"\u2A37"_ustr },
+ { u"Ouml"_ustr, u"\u00D6"_ustr },
+ { u"OverBar"_ustr, u"\u203E"_ustr },
+ { u"OverBrace"_ustr, u"\u23DE"_ustr },
+ { u"OverBracket"_ustr, u"\u23B4"_ustr },
+ { u"OverParenthesis"_ustr, u"\u23DC"_ustr },
+ { u"PartialD"_ustr, u"\u2202"_ustr },
+ { u"Pcy"_ustr, u"\u041F"_ustr },
+ { u"Pfr"_ustr, u"\U0001D513"_ustr },
+ { u"Phi"_ustr, u"\u03A6"_ustr },
+ { u"Pi"_ustr, u"\u03A0"_ustr },
+ { u"PlusMinus"_ustr, u"\u00B1"_ustr },
+ { u"Poincareplane"_ustr, u"\u210C"_ustr },
+ { u"Popf"_ustr, u"\u2119"_ustr },
+ { u"Pr"_ustr, u"\u2ABB"_ustr },
+ { u"Precedes"_ustr, u"\u227A"_ustr },
+ { u"PrecedesEqual"_ustr, u"\u2AAF"_ustr },
+ { u"PrecedesSlantEqual"_ustr, u"\u227C"_ustr },
+ { u"PrecedesTilde"_ustr, u"\u227E"_ustr },
+ { u"Prime"_ustr, u"\u2033"_ustr },
+ { u"Product"_ustr, u"\u220F"_ustr },
+ { u"Proportion"_ustr, u"\u2237"_ustr },
+ { u"Proportional"_ustr, u"\u221D"_ustr },
+ { u"Pscr"_ustr, u"\U0001D4AB"_ustr },
+ { u"Psi"_ustr, u"\u03A8"_ustr },
+ { u"QUOT"_ustr, u"\u0022"_ustr },
+ { u"Qfr"_ustr, u"\U0001D514"_ustr },
+ { u"Qopf"_ustr, u"\u211A"_ustr },
+ { u"Qscr"_ustr, u"\U0001D4AC"_ustr },
+ { u"RBarr"_ustr, u"\u2910"_ustr },
+ { u"REG"_ustr, u"\u00AE"_ustr },
+ { u"Racute"_ustr, u"\u0154"_ustr },
+ { u"Rang"_ustr, u"\u27EB"_ustr },
+ { u"Rarr"_ustr, u"\u21A0"_ustr },
+ { u"Rarrtl"_ustr, u"\u2916"_ustr },
+ { u"Rcaron"_ustr, u"\u0158"_ustr },
+ { u"Rcedil"_ustr, u"\u0156"_ustr },
+ { u"Rcy"_ustr, u"\u0420"_ustr },
+ { u"Re"_ustr, u"\u211C"_ustr },
+ { u"ReverseElement"_ustr, u"\u220B"_ustr },
+ { u"ReverseEquilibrium"_ustr, u"\u21CB"_ustr },
+ { u"ReverseUpEquilibrium"_ustr, u"\u296F"_ustr },
+ { u"Rfr"_ustr, u"\u211C"_ustr },
+ { u"Rho"_ustr, u"\u03A1"_ustr },
+ { u"RightAngleBracket"_ustr, u"\u27E9"_ustr },
+ { u"RightArrow"_ustr, u"\u2192"_ustr },
+ { u"RightArrowBar"_ustr, u"\u21E5"_ustr },
+ { u"RightArrowLeftArrow"_ustr, u"\u21C4"_ustr },
+ { u"RightCeiling"_ustr, u"\u2309"_ustr },
+ { u"RightDoubleBracket"_ustr, u"\u27E7"_ustr },
+ { u"RightDownTeeVector"_ustr, u"\u295D"_ustr },
+ { u"RightDownVector"_ustr, u"\u21C2"_ustr },
+ { u"RightDownVectorBar"_ustr, u"\u2955"_ustr },
+ { u"RightFloor"_ustr, u"\u230B"_ustr },
+ { u"RightTee"_ustr, u"\u22A2"_ustr },
+ { u"RightTeeArrow"_ustr, u"\u21A6"_ustr },
+ { u"RightTeeVector"_ustr, u"\u295B"_ustr },
+ { u"RightTriangle"_ustr, u"\u22B3"_ustr },
+ { u"RightTriangleBar"_ustr, u"\u29D0"_ustr },
+ { u"RightTriangleEqual"_ustr, u"\u22B5"_ustr },
+ { u"RightUpDownVector"_ustr, u"\u294F"_ustr },
+ { u"RightUpTeeVector"_ustr, u"\u295C"_ustr },
+ { u"RightUpVector"_ustr, u"\u21BE"_ustr },
+ { u"RightUpVectorBar"_ustr, u"\u2954"_ustr },
+ { u"RightVector"_ustr, u"\u21C0"_ustr },
+ { u"RightVectorBar"_ustr, u"\u2953"_ustr },
+ { u"Rightarrow"_ustr, u"\u21D2"_ustr },
+ { u"Ropf"_ustr, u"\u211D"_ustr },
+ { u"RoundImplies"_ustr, u"\u2970"_ustr },
+ { u"Rrightarrow"_ustr, u"\u21DB"_ustr },
+ { u"Rscr"_ustr, u"\u211B"_ustr },
+ { u"Rsh"_ustr, u"\u21B1"_ustr },
+ { u"RuleDelayed"_ustr, u"\u29F4"_ustr },
+ { u"SHCHcy"_ustr, u"\u0429"_ustr },
+ { u"SHcy"_ustr, u"\u0428"_ustr },
+ { u"SOFTcy"_ustr, u"\u042C"_ustr },
+ { u"Sacute"_ustr, u"\u015A"_ustr },
+ { u"Sc"_ustr, u"\u2ABC"_ustr },
+ { u"Scaron"_ustr, u"\u0160"_ustr },
+ { u"Scedil"_ustr, u"\u015E"_ustr },
+ { u"Scirc"_ustr, u"\u015C"_ustr },
+ { u"Scy"_ustr, u"\u0421"_ustr },
+ { u"Sfr"_ustr, u"\U0001D516"_ustr },
+ { u"ShortDownArrow"_ustr, u"\u2193"_ustr },
+ { u"ShortLeftArrow"_ustr, u"\u2190"_ustr },
+ { u"ShortRightArrow"_ustr, u"\u2192"_ustr },
+ { u"ShortUpArrow"_ustr, u"\u2191"_ustr },
+ { u"Sigma"_ustr, u"\u03A3"_ustr },
+ { u"SmallCircle"_ustr, u"\u2218"_ustr },
+ { u"Sopf"_ustr, u"\U0001D54A"_ustr },
+ { u"Sqrt"_ustr, u"\u221A"_ustr },
+ { u"Square"_ustr, u"\u25A1"_ustr },
+ { u"SquareIntersection"_ustr, u"\u2293"_ustr },
+ { u"SquareSubset"_ustr, u"\u228F"_ustr },
+ { u"SquareSubsetEqual"_ustr, u"\u2291"_ustr },
+ { u"SquareSuperset"_ustr, u"\u2290"_ustr },
+ { u"SquareSupersetEqual"_ustr, u"\u2292"_ustr },
+ { u"SquareUnion"_ustr, u"\u2294"_ustr },
+ { u"Sscr"_ustr, u"\U0001D4AE"_ustr },
+ { u"Star"_ustr, u"\u22C6"_ustr },
+ { u"Sub"_ustr, u"\u22D0"_ustr },
+ { u"Subset"_ustr, u"\u22D0"_ustr },
+ { u"SubsetEqual"_ustr, u"\u2286"_ustr },
+ { u"Succeeds"_ustr, u"\u227B"_ustr },
+ { u"SucceedsEqual"_ustr, u"\u2AB0"_ustr },
+ { u"SucceedsSlantEqual"_ustr, u"\u227D"_ustr },
+ { u"SucceedsTilde"_ustr, u"\u227F"_ustr },
+ { u"SuchThat"_ustr, u"\u220B"_ustr },
+ { u"Sum"_ustr, u"\u2211"_ustr },
+ { u"Sup"_ustr, u"\u22D1"_ustr },
+ { u"Superset"_ustr, u"\u2283"_ustr },
+ { u"SupersetEqual"_ustr, u"\u2287"_ustr },
+ { u"Supset"_ustr, u"\u22D1"_ustr },
+ { u"THORN"_ustr, u"\u00DE"_ustr },
+ { u"TRADE"_ustr, u"\u2122"_ustr },
+ { u"TSHcy"_ustr, u"\u040B"_ustr },
+ { u"TScy"_ustr, u"\u0426"_ustr },
+ { u"Tab"_ustr, u"\u0009"_ustr },
+ { u"Tau"_ustr, u"\u03A4"_ustr },
+ { u"Tcaron"_ustr, u"\u0164"_ustr },
+ { u"Tcedil"_ustr, u"\u0162"_ustr },
+ { u"Tcy"_ustr, u"\u0422"_ustr },
+ { u"Tfr"_ustr, u"\U0001D517"_ustr },
+ { u"Therefore"_ustr, u"\u2234"_ustr },
+ { u"Theta"_ustr, u"\u0398"_ustr },
+ { u"ThickSpace"_ustr, u"\u205F\u200A"_ustr },
+ { u"ThinSpace"_ustr, u"\u2009"_ustr },
+ { u"Tilde"_ustr, u"\u223C"_ustr },
+ { u"TildeEqual"_ustr, u"\u2243"_ustr },
+ { u"TildeFullEqual"_ustr, u"\u2245"_ustr },
+ { u"TildeTilde"_ustr, u"\u2248"_ustr },
+ { u"Topf"_ustr, u"\U0001D54B"_ustr },
+ { u"TripleDot"_ustr, u"\u20DB"_ustr },
+ { u"Tscr"_ustr, u"\U0001D4AF"_ustr },
+ { u"Tstrok"_ustr, u"\u0166"_ustr },
+ { u"Uacute"_ustr, u"\u00DA"_ustr },
+ { u"Uarr"_ustr, u"\u219F"_ustr },
+ { u"Uarrocir"_ustr, u"\u2949"_ustr },
+ { u"Ubrcy"_ustr, u"\u040E"_ustr },
+ { u"Ubreve"_ustr, u"\u016C"_ustr },
+ { u"Ucirc"_ustr, u"\u00DB"_ustr },
+ { u"Ucy"_ustr, u"\u0423"_ustr },
+ { u"Udblac"_ustr, u"\u0170"_ustr },
+ { u"Ufr"_ustr, u"\U0001D518"_ustr },
+ { u"Ugrave"_ustr, u"\u00D9"_ustr },
+ { u"Umacr"_ustr, u"\u016A"_ustr },
+ { u"UnderBar"_ustr, u"\u005F"_ustr },
+ { u"UnderBrace"_ustr, u"\u23DF"_ustr },
+ { u"UnderBracket"_ustr, u"\u23B5"_ustr },
+ { u"UnderParenthesis"_ustr, u"\u23DD"_ustr },
+ { u"Union"_ustr, u"\u22C3"_ustr },
+ { u"UnionPlus"_ustr, u"\u228E"_ustr },
+ { u"Uogon"_ustr, u"\u0172"_ustr },
+ { u"Uopf"_ustr, u"\U0001D54C"_ustr },
+ { u"UpArrow"_ustr, u"\u2191"_ustr },
+ { u"UpArrowBar"_ustr, u"\u2912"_ustr },
+ { u"UpArrowDownArrow"_ustr, u"\u21C5"_ustr },
+ { u"UpDownArrow"_ustr, u"\u2195"_ustr },
+ { u"UpEquilibrium"_ustr, u"\u296E"_ustr },
+ { u"UpTee"_ustr, u"\u22A5"_ustr },
+ { u"UpTeeArrow"_ustr, u"\u21A5"_ustr },
+ { u"Uparrow"_ustr, u"\u21D1"_ustr },
+ { u"Updownarrow"_ustr, u"\u21D5"_ustr },
+ { u"UpperLeftArrow"_ustr, u"\u2196"_ustr },
+ { u"UpperRightArrow"_ustr, u"\u2197"_ustr },
+ { u"Upsi"_ustr, u"\u03D2"_ustr },
+ { u"Upsilon"_ustr, u"\u03A5"_ustr },
+ { u"Uring"_ustr, u"\u016E"_ustr },
+ { u"Uscr"_ustr, u"\U0001D4B0"_ustr },
+ { u"Utilde"_ustr, u"\u0168"_ustr },
+ { u"Uuml"_ustr, u"\u00DC"_ustr },
+ { u"VDash"_ustr, u"\u22AB"_ustr },
+ { u"Vbar"_ustr, u"\u2AEB"_ustr },
+ { u"Vcy"_ustr, u"\u0412"_ustr },
+ { u"Vdash"_ustr, u"\u22A9"_ustr },
+ { u"Vdashl"_ustr, u"\u2AE6"_ustr },
+ { u"Vee"_ustr, u"\u22C1"_ustr },
+ { u"Verbar"_ustr, u"\u2016"_ustr },
+ { u"Vert"_ustr, u"\u2016"_ustr },
+ { u"VerticalBar"_ustr, u"\u2223"_ustr },
+ { u"VerticalLine"_ustr, u"\u007C"_ustr },
+ { u"VerticalSeparator"_ustr, u"\u2758"_ustr },
+ { u"VerticalTilde"_ustr, u"\u2240"_ustr },
+ { u"VeryThinSpace"_ustr, u"\u200A"_ustr },
+ { u"Vfr"_ustr, u"\U0001D519"_ustr },
+ { u"Vopf"_ustr, u"\U0001D54D"_ustr },
+ { u"Vscr"_ustr, u"\U0001D4B1"_ustr },
+ { u"Vvdash"_ustr, u"\u22AA"_ustr },
+ { u"Wcirc"_ustr, u"\u0174"_ustr },
+ { u"Wedge"_ustr, u"\u22C0"_ustr },
+ { u"Wfr"_ustr, u"\U0001D51A"_ustr },
+ { u"Wopf"_ustr, u"\U0001D54E"_ustr },
+ { u"Wscr"_ustr, u"\U0001D4B2"_ustr },
+ { u"Xfr"_ustr, u"\U0001D51B"_ustr },
+ { u"Xi"_ustr, u"\u039E"_ustr },
+ { u"Xopf"_ustr, u"\U0001D54F"_ustr },
+ { u"Xscr"_ustr, u"\U0001D4B3"_ustr },
+ { u"YAcy"_ustr, u"\u042F"_ustr },
+ { u"YIcy"_ustr, u"\u0407"_ustr },
+ { u"YUcy"_ustr, u"\u042E"_ustr },
+ { u"Yacute"_ustr, u"\u00DD"_ustr },
+ { u"Ycirc"_ustr, u"\u0176"_ustr },
+ { u"Ycy"_ustr, u"\u042B"_ustr },
+ { u"Yfr"_ustr, u"\U0001D51C"_ustr },
+ { u"Yopf"_ustr, u"\U0001D550"_ustr },
+ { u"Yscr"_ustr, u"\U0001D4B4"_ustr },
+ { u"Yuml"_ustr, u"\u0178"_ustr },
+ { u"ZHcy"_ustr, u"\u0416"_ustr },
+ { u"Zacute"_ustr, u"\u0179"_ustr },
+ { u"Zcaron"_ustr, u"\u017D"_ustr },
+ { u"Zcy"_ustr, u"\u0417"_ustr },
+ { u"Zdot"_ustr, u"\u017B"_ustr },
+ { u"ZeroWidthSpace"_ustr, u"\u200B"_ustr },
+ { u"Zeta"_ustr, u"\u0396"_ustr },
+ { u"Zfr"_ustr, u"\u2128"_ustr },
+ { u"Zopf"_ustr, u"\u2124"_ustr },
+ { u"Zscr"_ustr, u"\U0001D4B5"_ustr },
+ { u"aacute"_ustr, u"\u00E1"_ustr },
+ { u"abreve"_ustr, u"\u0103"_ustr },
+ { u"ac"_ustr, u"\u223E"_ustr },
+ { u"acE"_ustr, u"\u223E\u0333"_ustr },
+ { u"acd"_ustr, u"\u223F"_ustr },
+ { u"acirc"_ustr, u"\u00E2"_ustr },
+ { u"acute"_ustr, u"\u00B4"_ustr },
+ { u"acy"_ustr, u"\u0430"_ustr },
+ { u"aelig"_ustr, u"\u00E6"_ustr },
+ { u"af"_ustr, u"\u2061"_ustr },
+ { u"afr"_ustr, u"\U0001D51E"_ustr },
+ { u"agrave"_ustr, u"\u00E0"_ustr },
+ { u"alefsym"_ustr, u"\u2135"_ustr },
+ { u"aleph"_ustr, u"\u2135"_ustr },
+ { u"alpha"_ustr, u"\u03B1"_ustr },
+ { u"amacr"_ustr, u"\u0101"_ustr },
+ { u"amalg"_ustr, u"\u2A3F"_ustr },
+ { u"amp"_ustr, u"\u0026"_ustr },
+ { u"and"_ustr, u"\u2227"_ustr },
+ { u"andand"_ustr, u"\u2A55"_ustr },
+ { u"andd"_ustr, u"\u2A5C"_ustr },
+ { u"andslope"_ustr, u"\u2A58"_ustr },
+ { u"andv"_ustr, u"\u2A5A"_ustr },
+ { u"ang"_ustr, u"\u2220"_ustr },
+ { u"ange"_ustr, u"\u29A4"_ustr },
+ { u"angle"_ustr, u"\u2220"_ustr },
+ { u"angmsd"_ustr, u"\u2221"_ustr },
+ { u"angmsdaa"_ustr, u"\u29A8"_ustr },
+ { u"angmsdab"_ustr, u"\u29A9"_ustr },
+ { u"angmsdac"_ustr, u"\u29AA"_ustr },
+ { u"angmsdad"_ustr, u"\u29AB"_ustr },
+ { u"angmsdae"_ustr, u"\u29AC"_ustr },
+ { u"angmsdaf"_ustr, u"\u29AD"_ustr },
+ { u"angmsdag"_ustr, u"\u29AE"_ustr },
+ { u"angmsdah"_ustr, u"\u29AF"_ustr },
+ { u"angrt"_ustr, u"\u221F"_ustr },
+ { u"angrtvb"_ustr, u"\u22BE"_ustr },
+ { u"angrtvbd"_ustr, u"\u299D"_ustr },
+ { u"angsph"_ustr, u"\u2222"_ustr },
+ { u"angst"_ustr, u"\u00C5"_ustr },
+ { u"angzarr"_ustr, u"\u237C"_ustr },
+ { u"aogon"_ustr, u"\u0105"_ustr },
+ { u"aopf"_ustr, u"\U0001D552"_ustr },
+ { u"ap"_ustr, u"\u2248"_ustr },
+ { u"apE"_ustr, u"\u2A70"_ustr },
+ { u"apacir"_ustr, u"\u2A6F"_ustr },
+ { u"ape"_ustr, u"\u224A"_ustr },
+ { u"apid"_ustr, u"\u224B"_ustr },
+ { u"apos"_ustr, u"\u0027"_ustr },
+ { u"approx"_ustr, u"\u2248"_ustr },
+ { u"approxeq"_ustr, u"\u224A"_ustr },
+ { u"aring"_ustr, u"\u00E5"_ustr },
+ { u"ascr"_ustr, u"\U0001D4B6"_ustr },
+ { u"ast"_ustr, u"\u002A"_ustr },
+ { u"asymp"_ustr, u"\u2248"_ustr },
+ { u"asympeq"_ustr, u"\u224D"_ustr },
+ { u"atilde"_ustr, u"\u00E3"_ustr },
+ { u"auml"_ustr, u"\u00E4"_ustr },
+ { u"awconint"_ustr, u"\u2233"_ustr },
+ { u"awint"_ustr, u"\u2A11"_ustr },
+ { u"bNot"_ustr, u"\u2AED"_ustr },
+ { u"backcong"_ustr, u"\u224C"_ustr },
+ { u"backepsilon"_ustr, u"\u03F6"_ustr },
+ { u"backprime"_ustr, u"\u2035"_ustr },
+ { u"backsim"_ustr, u"\u223D"_ustr },
+ { u"backsimeq"_ustr, u"\u22CD"_ustr },
+ { u"barvee"_ustr, u"\u22BD"_ustr },
+ { u"barwed"_ustr, u"\u2305"_ustr },
+ { u"barwedge"_ustr, u"\u2305"_ustr },
+ { u"bbrk"_ustr, u"\u23B5"_ustr },
+ { u"bbrktbrk"_ustr, u"\u23B6"_ustr },
+ { u"bcong"_ustr, u"\u224C"_ustr },
+ { u"bcy"_ustr, u"\u0431"_ustr },
+ { u"bdquo"_ustr, u"\u201E"_ustr },
+ { u"becaus"_ustr, u"\u2235"_ustr },
+ { u"because"_ustr, u"\u2235"_ustr },
+ { u"bemptyv"_ustr, u"\u29B0"_ustr },
+ { u"bepsi"_ustr, u"\u03F6"_ustr },
+ { u"bernou"_ustr, u"\u212C"_ustr },
+ { u"beta"_ustr, u"\u03B2"_ustr },
+ { u"beth"_ustr, u"\u2136"_ustr },
+ { u"between"_ustr, u"\u226C"_ustr },
+ { u"bfr"_ustr, u"\U0001D51F"_ustr },
+ { u"bigcap"_ustr, u"\u22C2"_ustr },
+ { u"bigcirc"_ustr, u"\u25EF"_ustr },
+ { u"bigcup"_ustr, u"\u22C3"_ustr },
+ { u"bigodot"_ustr, u"\u2A00"_ustr },
+ { u"bigoplus"_ustr, u"\u2A01"_ustr },
+ { u"bigotimes"_ustr, u"\u2A02"_ustr },
+ { u"bigsqcup"_ustr, u"\u2A06"_ustr },
+ { u"bigstar"_ustr, u"\u2605"_ustr },
+ { u"bigtriangledown"_ustr, u"\u25BD"_ustr },
+ { u"bigtriangleup"_ustr, u"\u25B3"_ustr },
+ { u"biguplus"_ustr, u"\u2A04"_ustr },
+ { u"bigvee"_ustr, u"\u22C1"_ustr },
+ { u"bigwedge"_ustr, u"\u22C0"_ustr },
+ { u"bkarow"_ustr, u"\u290D"_ustr },
+ { u"blacklozenge"_ustr, u"\u29EB"_ustr },
+ { u"blacksquare"_ustr, u"\u25AA"_ustr },
+ { u"blacktriangle"_ustr, u"\u25B4"_ustr },
+ { u"blacktriangledown"_ustr, u"\u25BE"_ustr },
+ { u"blacktriangleleft"_ustr, u"\u25C2"_ustr },
+ { u"blacktriangleright"_ustr, u"\u25B8"_ustr },
+ { u"blank"_ustr, u"\u2423"_ustr },
+ { u"blk12"_ustr, u"\u2592"_ustr },
+ { u"blk14"_ustr, u"\u2591"_ustr },
+ { u"blk34"_ustr, u"\u2593"_ustr },
+ { u"block"_ustr, u"\u2588"_ustr },
+ { u"bne"_ustr, u"\u003D\u20E5"_ustr },
+ { u"bnequiv"_ustr, u"\u2261\u20E5"_ustr },
+ { u"bnot"_ustr, u"\u2310"_ustr },
+ { u"bopf"_ustr, u"\U0001D553"_ustr },
+ { u"bot"_ustr, u"\u22A5"_ustr },
+ { u"bottom"_ustr, u"\u22A5"_ustr },
+ { u"bowtie"_ustr, u"\u22C8"_ustr },
+ { u"boxDL"_ustr, u"\u2557"_ustr },
+ { u"boxDR"_ustr, u"\u2554"_ustr },
+ { u"boxDl"_ustr, u"\u2556"_ustr },
+ { u"boxDr"_ustr, u"\u2553"_ustr },
+ { u"boxH"_ustr, u"\u2550"_ustr },
+ { u"boxHD"_ustr, u"\u2566"_ustr },
+ { u"boxHU"_ustr, u"\u2569"_ustr },
+ { u"boxHd"_ustr, u"\u2564"_ustr },
+ { u"boxHu"_ustr, u"\u2567"_ustr },
+ { u"boxUL"_ustr, u"\u255D"_ustr },
+ { u"boxUR"_ustr, u"\u255A"_ustr },
+ { u"boxUl"_ustr, u"\u255C"_ustr },
+ { u"boxUr"_ustr, u"\u2559"_ustr },
+ { u"boxV"_ustr, u"\u2551"_ustr },
+ { u"boxVH"_ustr, u"\u256C"_ustr },
+ { u"boxVL"_ustr, u"\u2563"_ustr },
+ { u"boxVR"_ustr, u"\u2560"_ustr },
+ { u"boxVh"_ustr, u"\u256B"_ustr },
+ { u"boxVl"_ustr, u"\u2562"_ustr },
+ { u"boxVr"_ustr, u"\u255F"_ustr },
+ { u"boxbox"_ustr, u"\u29C9"_ustr },
+ { u"boxdL"_ustr, u"\u2555"_ustr },
+ { u"boxdR"_ustr, u"\u2552"_ustr },
+ { u"boxdl"_ustr, u"\u2510"_ustr },
+ { u"boxdr"_ustr, u"\u250C"_ustr },
+ { u"boxh"_ustr, u"\u2500"_ustr },
+ { u"boxhD"_ustr, u"\u2565"_ustr },
+ { u"boxhU"_ustr, u"\u2568"_ustr },
+ { u"boxhd"_ustr, u"\u252C"_ustr },
+ { u"boxhu"_ustr, u"\u2534"_ustr },
+ { u"boxminus"_ustr, u"\u229F"_ustr },
+ { u"boxplus"_ustr, u"\u229E"_ustr },
+ { u"boxtimes"_ustr, u"\u22A0"_ustr },
+ { u"boxuL"_ustr, u"\u255B"_ustr },
+ { u"boxuR"_ustr, u"\u2558"_ustr },
+ { u"boxul"_ustr, u"\u2518"_ustr },
+ { u"boxur"_ustr, u"\u2514"_ustr },
+ { u"boxv"_ustr, u"\u2502"_ustr },
+ { u"boxvH"_ustr, u"\u256A"_ustr },
+ { u"boxvL"_ustr, u"\u2561"_ustr },
+ { u"boxvR"_ustr, u"\u255E"_ustr },
+ { u"boxvh"_ustr, u"\u253C"_ustr },
+ { u"boxvl"_ustr, u"\u2524"_ustr },
+ { u"boxvr"_ustr, u"\u251C"_ustr },
+ { u"bprime"_ustr, u"\u2035"_ustr },
+ { u"breve"_ustr, u"\u02D8"_ustr },
+ { u"brvbar"_ustr, u"\u00A6"_ustr },
+ { u"bscr"_ustr, u"\U0001D4B7"_ustr },
+ { u"bsemi"_ustr, u"\u204F"_ustr },
+ { u"bsim"_ustr, u"\u223D"_ustr },
+ { u"bsime"_ustr, u"\u22CD"_ustr },
+ { u"bsol"_ustr, u"\u005C"_ustr },
+ { u"bsolb"_ustr, u"\u29C5"_ustr },
+ { u"bsolhsub"_ustr, u"\u27C8"_ustr },
+ { u"bull"_ustr, u"\u2022"_ustr },
+ { u"bullet"_ustr, u"\u2022"_ustr },
+ { u"bump"_ustr, u"\u224E"_ustr },
+ { u"bumpE"_ustr, u"\u2AAE"_ustr },
+ { u"bumpe"_ustr, u"\u224F"_ustr },
+ { u"bumpeq"_ustr, u"\u224F"_ustr },
+ { u"cacute"_ustr, u"\u0107"_ustr },
+ { u"cap"_ustr, u"\u2229"_ustr },
+ { u"capand"_ustr, u"\u2A44"_ustr },
+ { u"capbrcup"_ustr, u"\u2A49"_ustr },
+ { u"capcap"_ustr, u"\u2A4B"_ustr },
+ { u"capcup"_ustr, u"\u2A47"_ustr },
+ { u"capdot"_ustr, u"\u2A40"_ustr },
+ { u"caps"_ustr, u"\u2229\uFE00"_ustr },
+ { u"caret"_ustr, u"\u2041"_ustr },
+ { u"caron"_ustr, u"\u02C7"_ustr },
+ { u"ccaps"_ustr, u"\u2A4D"_ustr },
+ { u"ccaron"_ustr, u"\u010D"_ustr },
+ { u"ccedil"_ustr, u"\u00E7"_ustr },
+ { u"ccirc"_ustr, u"\u0109"_ustr },
+ { u"ccups"_ustr, u"\u2A4C"_ustr },
+ { u"ccupssm"_ustr, u"\u2A50"_ustr },
+ { u"cdot"_ustr, u"\u010B"_ustr },
+ { u"cedil"_ustr, u"\u00B8"_ustr },
+ { u"cemptyv"_ustr, u"\u29B2"_ustr },
+ { u"cent"_ustr, u"\u00A2"_ustr },
+ { u"centerdot"_ustr, u"\u00B7"_ustr },
+ { u"cfr"_ustr, u"\U0001D520"_ustr },
+ { u"chcy"_ustr, u"\u0447"_ustr },
+ { u"check"_ustr, u"\u2713"_ustr },
+ { u"checkmark"_ustr, u"\u2713"_ustr },
+ { u"chi"_ustr, u"\u03C7"_ustr },
+ { u"cir"_ustr, u"\u25CB"_ustr },
+ { u"cirE"_ustr, u"\u29C3"_ustr },
+ { u"circ"_ustr, u"\u02C6"_ustr },
+ { u"circeq"_ustr, u"\u2257"_ustr },
+ { u"circlearrowleft"_ustr, u"\u21BA"_ustr },
+ { u"circlearrowright"_ustr, u"\u21BB"_ustr },
+ { u"circledR"_ustr, u"\u00AE"_ustr },
+ { u"circledS"_ustr, u"\u24C8"_ustr },
+ { u"circledast"_ustr, u"\u229B"_ustr },
+ { u"circledcirc"_ustr, u"\u229A"_ustr },
+ { u"circleddash"_ustr, u"\u229D"_ustr },
+ { u"cire"_ustr, u"\u2257"_ustr },
+ { u"cirfnint"_ustr, u"\u2A10"_ustr },
+ { u"cirmid"_ustr, u"\u2AEF"_ustr },
+ { u"cirscir"_ustr, u"\u29C2"_ustr },
+ { u"clubs"_ustr, u"\u2663"_ustr },
+ { u"clubsuit"_ustr, u"\u2663"_ustr },
+ { u"colon"_ustr, u"\u003A"_ustr },
+ { u"colone"_ustr, u"\u2254"_ustr },
+ { u"coloneq"_ustr, u"\u2254"_ustr },
+ { u"comma"_ustr, u"\u002C"_ustr },
+ { u"commat"_ustr, u"\u0040"_ustr },
+ { u"comp"_ustr, u"\u2201"_ustr },
+ { u"compfn"_ustr, u"\u2218"_ustr },
+ { u"complement"_ustr, u"\u2201"_ustr },
+ { u"complexes"_ustr, u"\u2102"_ustr },
+ { u"cong"_ustr, u"\u2245"_ustr },
+ { u"congdot"_ustr, u"\u2A6D"_ustr },
+ { u"conint"_ustr, u"\u222E"_ustr },
+ { u"copf"_ustr, u"\U0001D554"_ustr },
+ { u"coprod"_ustr, u"\u2210"_ustr },
+ { u"copy"_ustr, u"\u00A9"_ustr },
+ { u"copysr"_ustr, u"\u2117"_ustr },
+ { u"crarr"_ustr, u"\u21B5"_ustr },
+ { u"cross"_ustr, u"\u2717"_ustr },
+ { u"cscr"_ustr, u"\U0001D4B8"_ustr },
+ { u"csub"_ustr, u"\u2ACF"_ustr },
+ { u"csube"_ustr, u"\u2AD1"_ustr },
+ { u"csup"_ustr, u"\u2AD0"_ustr },
+ { u"csupe"_ustr, u"\u2AD2"_ustr },
+ { u"ctdot"_ustr, u"\u22EF"_ustr },
+ { u"cudarrl"_ustr, u"\u2938"_ustr },
+ { u"cudarrr"_ustr, u"\u2935"_ustr },
+ { u"cuepr"_ustr, u"\u22DE"_ustr },
+ { u"cuesc"_ustr, u"\u22DF"_ustr },
+ { u"cularr"_ustr, u"\u21B6"_ustr },
+ { u"cularrp"_ustr, u"\u293D"_ustr },
+ { u"cup"_ustr, u"\u222A"_ustr },
+ { u"cupbrcap"_ustr, u"\u2A48"_ustr },
+ { u"cupcap"_ustr, u"\u2A46"_ustr },
+ { u"cupcup"_ustr, u"\u2A4A"_ustr },
+ { u"cupdot"_ustr, u"\u228D"_ustr },
+ { u"cupor"_ustr, u"\u2A45"_ustr },
+ { u"cups"_ustr, u"\u222A\uFE00"_ustr },
+ { u"curarr"_ustr, u"\u21B7"_ustr },
+ { u"curarrm"_ustr, u"\u293C"_ustr },
+ { u"curlyeqprec"_ustr, u"\u22DE"_ustr },
+ { u"curlyeqsucc"_ustr, u"\u22DF"_ustr },
+ { u"curlyvee"_ustr, u"\u22CE"_ustr },
+ { u"curlywedge"_ustr, u"\u22CF"_ustr },
+ { u"curren"_ustr, u"\u00A4"_ustr },
+ { u"curvearrowleft"_ustr, u"\u21B6"_ustr },
+ { u"curvearrowright"_ustr, u"\u21B7"_ustr },
+ { u"cuvee"_ustr, u"\u22CE"_ustr },
+ { u"cuwed"_ustr, u"\u22CF"_ustr },
+ { u"cwconint"_ustr, u"\u2232"_ustr },
+ { u"cwint"_ustr, u"\u2231"_ustr },
+ { u"cylcty"_ustr, u"\u232D"_ustr },
+ { u"dArr"_ustr, u"\u21D3"_ustr },
+ { u"dHar"_ustr, u"\u2965"_ustr },
+ { u"dagger"_ustr, u"\u2020"_ustr },
+ { u"daleth"_ustr, u"\u2138"_ustr },
+ { u"darr"_ustr, u"\u2193"_ustr },
+ { u"dash"_ustr, u"\u2010"_ustr },
+ { u"dashv"_ustr, u"\u22A3"_ustr },
+ { u"dbkarow"_ustr, u"\u290F"_ustr },
+ { u"dblac"_ustr, u"\u02DD"_ustr },
+ { u"dcaron"_ustr, u"\u010F"_ustr },
+ { u"dcy"_ustr, u"\u0434"_ustr },
+ { u"dd"_ustr, u"\u2146"_ustr },
+ { u"ddagger"_ustr, u"\u2021"_ustr },
+ { u"ddarr"_ustr, u"\u21CA"_ustr },
+ { u"ddotseq"_ustr, u"\u2A77"_ustr },
+ { u"deg"_ustr, u"\u00B0"_ustr },
+ { u"delta"_ustr, u"\u03B4"_ustr },
+ { u"demptyv"_ustr, u"\u29B1"_ustr },
+ { u"dfisht"_ustr, u"\u297F"_ustr },
+ { u"dfr"_ustr, u"\U0001D521"_ustr },
+ { u"dharl"_ustr, u"\u21C3"_ustr },
+ { u"dharr"_ustr, u"\u21C2"_ustr },
+ { u"diam"_ustr, u"\u22C4"_ustr },
+ { u"diamond"_ustr, u"\u22C4"_ustr },
+ { u"diamondsuit"_ustr, u"\u2666"_ustr },
+ { u"diams"_ustr, u"\u2666"_ustr },
+ { u"die"_ustr, u"\u00A8"_ustr },
+ { u"digamma"_ustr, u"\u03DD"_ustr },
+ { u"disin"_ustr, u"\u22F2"_ustr },
+ { u"div"_ustr, u"\u00F7"_ustr },
+ { u"divide"_ustr, u"\u00F7"_ustr },
+ { u"divideontimes"_ustr, u"\u22C7"_ustr },
+ { u"divonx"_ustr, u"\u22C7"_ustr },
+ { u"djcy"_ustr, u"\u0452"_ustr },
+ { u"dlcorn"_ustr, u"\u231E"_ustr },
+ { u"dlcrop"_ustr, u"\u230D"_ustr },
+ { u"dollar"_ustr, u"\u0024"_ustr },
+ { u"dopf"_ustr, u"\U0001D555"_ustr },
+ { u"dot"_ustr, u"\u02D9"_ustr },
+ { u"doteq"_ustr, u"\u2250"_ustr },
+ { u"doteqdot"_ustr, u"\u2251"_ustr },
+ { u"dotminus"_ustr, u"\u2238"_ustr },
+ { u"dotplus"_ustr, u"\u2214"_ustr },
+ { u"dotsquare"_ustr, u"\u22A1"_ustr },
+ { u"doublebarwedge"_ustr, u"\u2306"_ustr },
+ { u"downarrow"_ustr, u"\u2193"_ustr },
+ { u"downdownarrows"_ustr, u"\u21CA"_ustr },
+ { u"downharpoonleft"_ustr, u"\u21C3"_ustr },
+ { u"downharpoonright"_ustr, u"\u21C2"_ustr },
+ { u"drbkarow"_ustr, u"\u2910"_ustr },
+ { u"drcorn"_ustr, u"\u231F"_ustr },
+ { u"drcrop"_ustr, u"\u230C"_ustr },
+ { u"dscr"_ustr, u"\U0001D4B9"_ustr },
+ { u"dscy"_ustr, u"\u0455"_ustr },
+ { u"dsol"_ustr, u"\u29F6"_ustr },
+ { u"dstrok"_ustr, u"\u0111"_ustr },
+ { u"dtdot"_ustr, u"\u22F1"_ustr },
+ { u"dtri"_ustr, u"\u25BF"_ustr },
+ { u"dtrif"_ustr, u"\u25BE"_ustr },
+ { u"duarr"_ustr, u"\u21F5"_ustr },
+ { u"duhar"_ustr, u"\u296F"_ustr },
+ { u"dwangle"_ustr, u"\u29A6"_ustr },
+ { u"dzcy"_ustr, u"\u045F"_ustr },
+ { u"dzigrarr"_ustr, u"\u27FF"_ustr },
+ { u"eDDot"_ustr, u"\u2A77"_ustr },
+ { u"eDot"_ustr, u"\u2251"_ustr },
+ { u"eacute"_ustr, u"\u00E9"_ustr },
+ { u"easter"_ustr, u"\u2A6E"_ustr },
+ { u"ecaron"_ustr, u"\u011B"_ustr },
+ { u"ecir"_ustr, u"\u2256"_ustr },
+ { u"ecirc"_ustr, u"\u00EA"_ustr },
+ { u"ecolon"_ustr, u"\u2255"_ustr },
+ { u"ecy"_ustr, u"\u044D"_ustr },
+ { u"edot"_ustr, u"\u0117"_ustr },
+ { u"ee"_ustr, u"\u2147"_ustr },
+ { u"efDot"_ustr, u"\u2252"_ustr },
+ { u"efr"_ustr, u"\U0001D522"_ustr },
+ { u"eg"_ustr, u"\u2A9A"_ustr },
+ { u"egrave"_ustr, u"\u00E8"_ustr },
+ { u"egs"_ustr, u"\u2A96"_ustr },
+ { u"egsdot"_ustr, u"\u2A98"_ustr },
+ { u"el"_ustr, u"\u2A99"_ustr },
+ { u"elinters"_ustr, u"\u23E7"_ustr },
+ { u"ell"_ustr, u"\u2113"_ustr },
+ { u"els"_ustr, u"\u2A95"_ustr },
+ { u"elsdot"_ustr, u"\u2A97"_ustr },
+ { u"emacr"_ustr, u"\u0113"_ustr },
+ { u"empty"_ustr, u"\u2205"_ustr },
+ { u"emptyset"_ustr, u"\u2205"_ustr },
+ { u"emptyv"_ustr, u"\u2205"_ustr },
+ { u"emsp"_ustr, u"\u2003"_ustr },
+ { u"emsp13"_ustr, u"\u2004"_ustr },
+ { u"emsp14"_ustr, u"\u2005"_ustr },
+ { u"eng"_ustr, u"\u014B"_ustr },
+ { u"ensp"_ustr, u"\u2002"_ustr },
+ { u"eogon"_ustr, u"\u0119"_ustr },
+ { u"eopf"_ustr, u"\U0001D556"_ustr },
+ { u"epar"_ustr, u"\u22D5"_ustr },
+ { u"eparsl"_ustr, u"\u29E3"_ustr },
+ { u"eplus"_ustr, u"\u2A71"_ustr },
+ { u"epsi"_ustr, u"\u03B5"_ustr },
+ { u"epsilon"_ustr, u"\u03B5"_ustr },
+ { u"epsiv"_ustr, u"\u03F5"_ustr },
+ { u"eqcirc"_ustr, u"\u2256"_ustr },
+ { u"eqcolon"_ustr, u"\u2255"_ustr },
+ { u"eqsim"_ustr, u"\u2242"_ustr },
+ { u"eqslantgtr"_ustr, u"\u2A96"_ustr },
+ { u"eqslantless"_ustr, u"\u2A95"_ustr },
+ { u"equals"_ustr, u"\u003D"_ustr },
+ { u"equest"_ustr, u"\u225F"_ustr },
+ { u"equiv"_ustr, u"\u2261"_ustr },
+ { u"equivDD"_ustr, u"\u2A78"_ustr },
+ { u"eqvparsl"_ustr, u"\u29E5"_ustr },
+ { u"erDot"_ustr, u"\u2253"_ustr },
+ { u"erarr"_ustr, u"\u2971"_ustr },
+ { u"escr"_ustr, u"\u212F"_ustr },
+ { u"esdot"_ustr, u"\u2250"_ustr },
+ { u"esim"_ustr, u"\u2242"_ustr },
+ { u"eta"_ustr, u"\u03B7"_ustr },
+ { u"eth"_ustr, u"\u00F0"_ustr },
+ { u"euml"_ustr, u"\u00EB"_ustr },
+ { u"euro"_ustr, u"\u20AC"_ustr },
+ { u"excl"_ustr, u"\u0021"_ustr },
+ { u"exist"_ustr, u"\u2203"_ustr },
+ { u"expectation"_ustr, u"\u2130"_ustr },
+ { u"exponentiale"_ustr, u"\u2147"_ustr },
+ { u"fallingdotseq"_ustr, u"\u2252"_ustr },
+ { u"fcy"_ustr, u"\u0444"_ustr },
+ { u"female"_ustr, u"\u2640"_ustr },
+ { u"ffilig"_ustr, u"\uFB03"_ustr },
+ { u"fflig"_ustr, u"\uFB00"_ustr },
+ { u"ffllig"_ustr, u"\uFB04"_ustr },
+ { u"ffr"_ustr, u"\U0001D523"_ustr },
+ { u"filig"_ustr, u"\uFB01"_ustr },
+ { u"fjlig"_ustr, u"\u0066\u006A"_ustr },
+ { u"flat"_ustr, u"\u266D"_ustr },
+ { u"fllig"_ustr, u"\uFB02"_ustr },
+ { u"fltns"_ustr, u"\u25B1"_ustr },
+ { u"fnof"_ustr, u"\u0192"_ustr },
+ { u"fopf"_ustr, u"\U0001D557"_ustr },
+ { u"forall"_ustr, u"\u2200"_ustr },
+ { u"fork"_ustr, u"\u22D4"_ustr },
+ { u"forkv"_ustr, u"\u2AD9"_ustr },
+ { u"fpartint"_ustr, u"\u2A0D"_ustr },
+ { u"frac12"_ustr, u"\u00BD"_ustr },
+ { u"frac13"_ustr, u"\u2153"_ustr },
+ { u"frac14"_ustr, u"\u00BC"_ustr },
+ { u"frac15"_ustr, u"\u2155"_ustr },
+ { u"frac16"_ustr, u"\u2159"_ustr },
+ { u"frac18"_ustr, u"\u215B"_ustr },
+ { u"frac23"_ustr, u"\u2154"_ustr },
+ { u"frac25"_ustr, u"\u2156"_ustr },
+ { u"frac34"_ustr, u"\u00BE"_ustr },
+ { u"frac35"_ustr, u"\u2157"_ustr },
+ { u"frac38"_ustr, u"\u215C"_ustr },
+ { u"frac45"_ustr, u"\u2158"_ustr },
+ { u"frac56"_ustr, u"\u215A"_ustr },
+ { u"frac58"_ustr, u"\u215D"_ustr },
+ { u"frac78"_ustr, u"\u215E"_ustr },
+ { u"frasl"_ustr, u"\u2044"_ustr },
+ { u"frown"_ustr, u"\u2322"_ustr },
+ { u"fscr"_ustr, u"\U0001D4BB"_ustr },
+ { u"gE"_ustr, u"\u2267"_ustr },
+ { u"gEl"_ustr, u"\u2A8C"_ustr },
+ { u"gacute"_ustr, u"\u01F5"_ustr },
+ { u"gamma"_ustr, u"\u03B3"_ustr },
+ { u"gammad"_ustr, u"\u03DD"_ustr },
+ { u"gap"_ustr, u"\u2A86"_ustr },
+ { u"gbreve"_ustr, u"\u011F"_ustr },
+ { u"gcirc"_ustr, u"\u011D"_ustr },
+ { u"gcy"_ustr, u"\u0433"_ustr },
+ { u"gdot"_ustr, u"\u0121"_ustr },
+ { u"ge"_ustr, u"\u2265"_ustr },
+ { u"gel"_ustr, u"\u22DB"_ustr },
+ { u"geq"_ustr, u"\u2265"_ustr },
+ { u"geqq"_ustr, u"\u2267"_ustr },
+ { u"geqslant"_ustr, u"\u2A7E"_ustr },
+ { u"ges"_ustr, u"\u2A7E"_ustr },
+ { u"gescc"_ustr, u"\u2AA9"_ustr },
+ { u"gesdot"_ustr, u"\u2A80"_ustr },
+ { u"gesdoto"_ustr, u"\u2A82"_ustr },
+ { u"gesdotol"_ustr, u"\u2A84"_ustr },
+ { u"gesl"_ustr, u"\u22DB\uFE00"_ustr },
+ { u"gesles"_ustr, u"\u2A94"_ustr },
+ { u"gfr"_ustr, u"\U0001D524"_ustr },
+ { u"gg"_ustr, u"\u226B"_ustr },
+ { u"ggg"_ustr, u"\u22D9"_ustr },
+ { u"gimel"_ustr, u"\u2137"_ustr },
+ { u"gjcy"_ustr, u"\u0453"_ustr },
+ { u"gl"_ustr, u"\u2277"_ustr },
+ { u"glE"_ustr, u"\u2A92"_ustr },
+ { u"gla"_ustr, u"\u2AA5"_ustr },
+ { u"glj"_ustr, u"\u2AA4"_ustr },
+ { u"gnE"_ustr, u"\u2269"_ustr },
+ { u"gnap"_ustr, u"\u2A8A"_ustr },
+ { u"gnapprox"_ustr, u"\u2A8A"_ustr },
+ { u"gne"_ustr, u"\u2A88"_ustr },
+ { u"gneq"_ustr, u"\u2A88"_ustr },
+ { u"gneqq"_ustr, u"\u2269"_ustr },
+ { u"gnsim"_ustr, u"\u22E7"_ustr },
+ { u"gopf"_ustr, u"\U0001D558"_ustr },
+ { u"grave"_ustr, u"\u0060"_ustr },
+ { u"gscr"_ustr, u"\u210A"_ustr },
+ { u"gsim"_ustr, u"\u2273"_ustr },
+ { u"gsime"_ustr, u"\u2A8E"_ustr },
+ { u"gsiml"_ustr, u"\u2A90"_ustr },
+ { u"gt"_ustr, u"\u003E"_ustr },
+ { u"gtcc"_ustr, u"\u2AA7"_ustr },
+ { u"gtcir"_ustr, u"\u2A7A"_ustr },
+ { u"gtdot"_ustr, u"\u22D7"_ustr },
+ { u"gtlPar"_ustr, u"\u2995"_ustr },
+ { u"gtquest"_ustr, u"\u2A7C"_ustr },
+ { u"gtrapprox"_ustr, u"\u2A86"_ustr },
+ { u"gtrarr"_ustr, u"\u2978"_ustr },
+ { u"gtrdot"_ustr, u"\u22D7"_ustr },
+ { u"gtreqless"_ustr, u"\u22DB"_ustr },
+ { u"gtreqqless"_ustr, u"\u2A8C"_ustr },
+ { u"gtrless"_ustr, u"\u2277"_ustr },
+ { u"gtrsim"_ustr, u"\u2273"_ustr },
+ { u"gvertneqq"_ustr, u"\u2269\uFE00"_ustr },
+ { u"gvnE"_ustr, u"\u2269\uFE00"_ustr },
+ { u"hArr"_ustr, u"\u21D4"_ustr },
+ { u"hairsp"_ustr, u"\u200A"_ustr },
+ { u"half"_ustr, u"\u00BD"_ustr },
+ { u"hamilt"_ustr, u"\u210B"_ustr },
+ { u"hardcy"_ustr, u"\u044A"_ustr },
+ { u"harr"_ustr, u"\u2194"_ustr },
+ { u"harrcir"_ustr, u"\u2948"_ustr },
+ { u"harrw"_ustr, u"\u21AD"_ustr },
+ { u"hbar"_ustr, u"\u210F"_ustr },
+ { u"hcirc"_ustr, u"\u0125"_ustr },
+ { u"hearts"_ustr, u"\u2665"_ustr },
+ { u"heartsuit"_ustr, u"\u2665"_ustr },
+ { u"hellip"_ustr, u"\u2026"_ustr },
+ { u"hercon"_ustr, u"\u22B9"_ustr },
+ { u"hfr"_ustr, u"\U0001D525"_ustr },
+ { u"hksearow"_ustr, u"\u2925"_ustr },
+ { u"hkswarow"_ustr, u"\u2926"_ustr },
+ { u"hoarr"_ustr, u"\u21FF"_ustr },
+ { u"homtht"_ustr, u"\u223B"_ustr },
+ { u"hookleftarrow"_ustr, u"\u21A9"_ustr },
+ { u"hookrightarrow"_ustr, u"\u21AA"_ustr },
+ { u"hopf"_ustr, u"\U0001D559"_ustr },
+ { u"horbar"_ustr, u"\u2015"_ustr },
+ { u"hscr"_ustr, u"\U0001D4BD"_ustr },
+ { u"hslash"_ustr, u"\u210F"_ustr },
+ { u"hstrok"_ustr, u"\u0127"_ustr },
+ { u"hybull"_ustr, u"\u2043"_ustr },
+ { u"hyphen"_ustr, u"\u2010"_ustr },
+ { u"iacute"_ustr, u"\u00ED"_ustr },
+ { u"ic"_ustr, u"\u2063"_ustr },
+ { u"icirc"_ustr, u"\u00EE"_ustr },
+ { u"icy"_ustr, u"\u0438"_ustr },
+ { u"iecy"_ustr, u"\u0435"_ustr },
+ { u"iexcl"_ustr, u"\u00A1"_ustr },
+ { u"iff"_ustr, u"\u21D4"_ustr },
+ { u"ifr"_ustr, u"\U0001D526"_ustr },
+ { u"igrave"_ustr, u"\u00EC"_ustr },
+ { u"ii"_ustr, u"\u2148"_ustr },
+ { u"iiiint"_ustr, u"\u2A0C"_ustr },
+ { u"iiint"_ustr, u"\u222D"_ustr },
+ { u"iinfin"_ustr, u"\u29DC"_ustr },
+ { u"iiota"_ustr, u"\u2129"_ustr },
+ { u"ijlig"_ustr, u"\u0133"_ustr },
+ { u"imacr"_ustr, u"\u012B"_ustr },
+ { u"image"_ustr, u"\u2111"_ustr },
+ { u"imagline"_ustr, u"\u2110"_ustr },
+ { u"imagpart"_ustr, u"\u2111"_ustr },
+ { u"imath"_ustr, u"\u0131"_ustr },
+ { u"imof"_ustr, u"\u22B7"_ustr },
+ { u"imped"_ustr, u"\u01B5"_ustr },
+ { u"in"_ustr, u"\u2208"_ustr },
+ { u"incare"_ustr, u"\u2105"_ustr },
+ { u"infin"_ustr, u"\u221E"_ustr },
+ { u"infintie"_ustr, u"\u29DD"_ustr },
+ { u"inodot"_ustr, u"\u0131"_ustr },
+ { u"int"_ustr, u"\u222B"_ustr },
+ { u"intcal"_ustr, u"\u22BA"_ustr },
+ { u"integers"_ustr, u"\u2124"_ustr },
+ { u"intercal"_ustr, u"\u22BA"_ustr },
+ { u"intlarhk"_ustr, u"\u2A17"_ustr },
+ { u"intprod"_ustr, u"\u2A3C"_ustr },
+ { u"iocy"_ustr, u"\u0451"_ustr },
+ { u"iogon"_ustr, u"\u012F"_ustr },
+ { u"iopf"_ustr, u"\U0001D55A"_ustr },
+ { u"iota"_ustr, u"\u03B9"_ustr },
+ { u"iprod"_ustr, u"\u2A3C"_ustr },
+ { u"iquest"_ustr, u"\u00BF"_ustr },
+ { u"iscr"_ustr, u"\U0001D4BE"_ustr },
+ { u"isin"_ustr, u"\u2208"_ustr },
+ { u"isinE"_ustr, u"\u22F9"_ustr },
+ { u"isindot"_ustr, u"\u22F5"_ustr },
+ { u"isins"_ustr, u"\u22F4"_ustr },
+ { u"isinsv"_ustr, u"\u22F3"_ustr },
+ { u"isinv"_ustr, u"\u2208"_ustr },
+ { u"it"_ustr, u"\u2062"_ustr },
+ { u"itilde"_ustr, u"\u0129"_ustr },
+ { u"iukcy"_ustr, u"\u0456"_ustr },
+ { u"iuml"_ustr, u"\u00EF"_ustr },
+ { u"jcirc"_ustr, u"\u0135"_ustr },
+ { u"jcy"_ustr, u"\u0439"_ustr },
+ { u"jfr"_ustr, u"\U0001D527"_ustr },
+ { u"jmath"_ustr, u"\u0237"_ustr },
+ { u"jopf"_ustr, u"\U0001D55B"_ustr },
+ { u"jscr"_ustr, u"\U0001D4BF"_ustr },
+ { u"jsercy"_ustr, u"\u0458"_ustr },
+ { u"jukcy"_ustr, u"\u0454"_ustr },
+ { u"kappa"_ustr, u"\u03BA"_ustr },
+ { u"kappav"_ustr, u"\u03F0"_ustr },
+ { u"kcedil"_ustr, u"\u0137"_ustr },
+ { u"kcy"_ustr, u"\u043A"_ustr },
+ { u"kfr"_ustr, u"\U0001D528"_ustr },
+ { u"kgreen"_ustr, u"\u0138"_ustr },
+ { u"khcy"_ustr, u"\u0445"_ustr },
+ { u"kjcy"_ustr, u"\u045C"_ustr },
+ { u"kopf"_ustr, u"\U0001D55C"_ustr },
+ { u"kscr"_ustr, u"\U0001D4C0"_ustr },
+ { u"lAarr"_ustr, u"\u21DA"_ustr },
+ { u"lArr"_ustr, u"\u21D0"_ustr },
+ { u"lAtail"_ustr, u"\u291B"_ustr },
+ { u"lBarr"_ustr, u"\u290E"_ustr },
+ { u"lE"_ustr, u"\u2266"_ustr },
+ { u"lEg"_ustr, u"\u2A8B"_ustr },
+ { u"lHar"_ustr, u"\u2962"_ustr },
+ { u"lacute"_ustr, u"\u013A"_ustr },
+ { u"laemptyv"_ustr, u"\u29B4"_ustr },
+ { u"lagran"_ustr, u"\u2112"_ustr },
+ { u"lambda"_ustr, u"\u03BB"_ustr },
+ { u"lang"_ustr, u"\u27E8"_ustr },
+ { u"langd"_ustr, u"\u2991"_ustr },
+ { u"langle"_ustr, u"\u27E8"_ustr },
+ { u"lap"_ustr, u"\u2A85"_ustr },
+ { u"laquo"_ustr, u"\u00AB"_ustr },
+ { u"larr"_ustr, u"\u2190"_ustr },
+ { u"larrb"_ustr, u"\u21E4"_ustr },
+ { u"larrbfs"_ustr, u"\u291F"_ustr },
+ { u"larrfs"_ustr, u"\u291D"_ustr },
+ { u"larrhk"_ustr, u"\u21A9"_ustr },
+ { u"larrlp"_ustr, u"\u21AB"_ustr },
+ { u"larrpl"_ustr, u"\u2939"_ustr },
+ { u"larrsim"_ustr, u"\u2973"_ustr },
+ { u"larrtl"_ustr, u"\u21A2"_ustr },
+ { u"lat"_ustr, u"\u2AAB"_ustr },
+ { u"latail"_ustr, u"\u2919"_ustr },
+ { u"late"_ustr, u"\u2AAD"_ustr },
+ { u"lates"_ustr, u"\u2AAD\uFE00"_ustr },
+ { u"lbarr"_ustr, u"\u290C"_ustr },
+ { u"lbbrk"_ustr, u"\u2772"_ustr },
+ { u"lbrace"_ustr, u"\u007B"_ustr },
+ { u"lbrack"_ustr, u"\u005B"_ustr },
+ { u"lbrke"_ustr, u"\u298B"_ustr },
+ { u"lbrksld"_ustr, u"\u298F"_ustr },
+ { u"lbrkslu"_ustr, u"\u298D"_ustr },
+ { u"lcaron"_ustr, u"\u013E"_ustr },
+ { u"lcedil"_ustr, u"\u013C"_ustr },
+ { u"lceil"_ustr, u"\u2308"_ustr },
+ { u"lcub"_ustr, u"\u007B"_ustr },
+ { u"lcy"_ustr, u"\u043B"_ustr },
+ { u"ldca"_ustr, u"\u2936"_ustr },
+ { u"ldquo"_ustr, u"\u201C"_ustr },
+ { u"ldquor"_ustr, u"\u201E"_ustr },
+ { u"ldrdhar"_ustr, u"\u2967"_ustr },
+ { u"ldrushar"_ustr, u"\u294B"_ustr },
+ { u"ldsh"_ustr, u"\u21B2"_ustr },
+ { u"le"_ustr, u"\u2264"_ustr },
+ { u"leftarrow"_ustr, u"\u2190"_ustr },
+ { u"leftarrowtail"_ustr, u"\u21A2"_ustr },
+ { u"leftharpoondown"_ustr, u"\u21BD"_ustr },
+ { u"leftharpoonup"_ustr, u"\u21BC"_ustr },
+ { u"leftleftarrows"_ustr, u"\u21C7"_ustr },
+ { u"leftrightarrow"_ustr, u"\u2194"_ustr },
+ { u"leftrightarrows"_ustr, u"\u21C6"_ustr },
+ { u"leftrightharpoons"_ustr, u"\u21CB"_ustr },
+ { u"leftrightsquigarrow"_ustr, u"\u21AD"_ustr },
+ { u"leftthreetimes"_ustr, u"\u22CB"_ustr },
+ { u"leg"_ustr, u"\u22DA"_ustr },
+ { u"leq"_ustr, u"\u2264"_ustr },
+ { u"leqq"_ustr, u"\u2266"_ustr },
+ { u"leqslant"_ustr, u"\u2A7D"_ustr },
+ { u"les"_ustr, u"\u2A7D"_ustr },
+ { u"lescc"_ustr, u"\u2AA8"_ustr },
+ { u"lesdot"_ustr, u"\u2A7F"_ustr },
+ { u"lesdoto"_ustr, u"\u2A81"_ustr },
+ { u"lesdotor"_ustr, u"\u2A83"_ustr },
+ { u"lesg"_ustr, u"\u22DA\uFE00"_ustr },
+ { u"lesges"_ustr, u"\u2A93"_ustr },
+ { u"lessapprox"_ustr, u"\u2A85"_ustr },
+ { u"lessdot"_ustr, u"\u22D6"_ustr },
+ { u"lesseqgtr"_ustr, u"\u22DA"_ustr },
+ { u"lesseqqgtr"_ustr, u"\u2A8B"_ustr },
+ { u"lessgtr"_ustr, u"\u2276"_ustr },
+ { u"lesssim"_ustr, u"\u2272"_ustr },
+ { u"lfisht"_ustr, u"\u297C"_ustr },
+ { u"lfloor"_ustr, u"\u230A"_ustr },
+ { u"lfr"_ustr, u"\U0001D529"_ustr },
+ { u"lg"_ustr, u"\u2276"_ustr },
+ { u"lgE"_ustr, u"\u2A91"_ustr },
+ { u"lhard"_ustr, u"\u21BD"_ustr },
+ { u"lharu"_ustr, u"\u21BC"_ustr },
+ { u"lharul"_ustr, u"\u296A"_ustr },
+ { u"lhblk"_ustr, u"\u2584"_ustr },
+ { u"ljcy"_ustr, u"\u0459"_ustr },
+ { u"ll"_ustr, u"\u226A"_ustr },
+ { u"llarr"_ustr, u"\u21C7"_ustr },
+ { u"llcorner"_ustr, u"\u231E"_ustr },
+ { u"llhard"_ustr, u"\u296B"_ustr },
+ { u"lltri"_ustr, u"\u25FA"_ustr },
+ { u"lmidot"_ustr, u"\u0140"_ustr },
+ { u"lmoust"_ustr, u"\u23B0"_ustr },
+ { u"lmoustache"_ustr, u"\u23B0"_ustr },
+ { u"lnE"_ustr, u"\u2268"_ustr },
+ { u"lnap"_ustr, u"\u2A89"_ustr },
+ { u"lnapprox"_ustr, u"\u2A89"_ustr },
+ { u"lne"_ustr, u"\u2A87"_ustr },
+ { u"lneq"_ustr, u"\u2A87"_ustr },
+ { u"lneqq"_ustr, u"\u2268"_ustr },
+ { u"lnsim"_ustr, u"\u22E6"_ustr },
+ { u"loang"_ustr, u"\u27EC"_ustr },
+ { u"loarr"_ustr, u"\u21FD"_ustr },
+ { u"lobrk"_ustr, u"\u27E6"_ustr },
+ { u"longleftarrow"_ustr, u"\u27F5"_ustr },
+ { u"longleftrightarrow"_ustr, u"\u27F7"_ustr },
+ { u"longmapsto"_ustr, u"\u27FC"_ustr },
+ { u"longrightarrow"_ustr, u"\u27F6"_ustr },
+ { u"looparrowleft"_ustr, u"\u21AB"_ustr },
+ { u"looparrowright"_ustr, u"\u21AC"_ustr },
+ { u"lopar"_ustr, u"\u2985"_ustr },
+ { u"lopf"_ustr, u"\U0001D55D"_ustr },
+ { u"loplus"_ustr, u"\u2A2D"_ustr },
+ { u"lotimes"_ustr, u"\u2A34"_ustr },
+ { u"lowast"_ustr, u"\u2217"_ustr },
+ { u"lowbar"_ustr, u"\u005F"_ustr },
+ { u"loz"_ustr, u"\u25CA"_ustr },
+ { u"lozenge"_ustr, u"\u25CA"_ustr },
+ { u"lozf"_ustr, u"\u29EB"_ustr },
+ { u"lpar"_ustr, u"\u0028"_ustr },
+ { u"lparlt"_ustr, u"\u2993"_ustr },
+ { u"lrarr"_ustr, u"\u21C6"_ustr },
+ { u"lrcorner"_ustr, u"\u231F"_ustr },
+ { u"lrhar"_ustr, u"\u21CB"_ustr },
+ { u"lrhard"_ustr, u"\u296D"_ustr },
+ { u"lrm"_ustr, u"\u200E"_ustr },
+ { u"lrtri"_ustr, u"\u22BF"_ustr },
+ { u"lsaquo"_ustr, u"\u2039"_ustr },
+ { u"lscr"_ustr, u"\U0001D4C1"_ustr },
+ { u"lsh"_ustr, u"\u21B0"_ustr },
+ { u"lsim"_ustr, u"\u2272"_ustr },
+ { u"lsime"_ustr, u"\u2A8D"_ustr },
+ { u"lsimg"_ustr, u"\u2A8F"_ustr },
+ { u"lsqb"_ustr, u"\u005B"_ustr },
+ { u"lsquo"_ustr, u"\u2018"_ustr },
+ { u"lsquor"_ustr, u"\u201A"_ustr },
+ { u"lstrok"_ustr, u"\u0142"_ustr },
+ { u"lt"_ustr, u"\u003C"_ustr },
+ { u"ltcc"_ustr, u"\u2AA6"_ustr },
+ { u"ltcir"_ustr, u"\u2A79"_ustr },
+ { u"ltdot"_ustr, u"\u22D6"_ustr },
+ { u"lthree"_ustr, u"\u22CB"_ustr },
+ { u"ltimes"_ustr, u"\u22C9"_ustr },
+ { u"ltlarr"_ustr, u"\u2976"_ustr },
+ { u"ltquest"_ustr, u"\u2A7B"_ustr },
+ { u"ltrPar"_ustr, u"\u2996"_ustr },
+ { u"ltri"_ustr, u"\u25C3"_ustr },
+ { u"ltrie"_ustr, u"\u22B4"_ustr },
+ { u"ltrif"_ustr, u"\u25C2"_ustr },
+ { u"lurdshar"_ustr, u"\u294A"_ustr },
+ { u"luruhar"_ustr, u"\u2966"_ustr },
+ { u"lvertneqq"_ustr, u"\u2268\uFE00"_ustr },
+ { u"lvnE"_ustr, u"\u2268\uFE00"_ustr },
+ { u"mDDot"_ustr, u"\u223A"_ustr },
+ { u"macr"_ustr, u"\u00AF"_ustr },
+ { u"male"_ustr, u"\u2642"_ustr },
+ { u"malt"_ustr, u"\u2720"_ustr },
+ { u"maltese"_ustr, u"\u2720"_ustr },
+ { u"map"_ustr, u"\u21A6"_ustr },
+ { u"mapsto"_ustr, u"\u21A6"_ustr },
+ { u"mapstodown"_ustr, u"\u21A7"_ustr },
+ { u"mapstoleft"_ustr, u"\u21A4"_ustr },
+ { u"mapstoup"_ustr, u"\u21A5"_ustr },
+ { u"marker"_ustr, u"\u25AE"_ustr },
+ { u"mcomma"_ustr, u"\u2A29"_ustr },
+ { u"mcy"_ustr, u"\u043C"_ustr },
+ { u"mdash"_ustr, u"\u2014"_ustr },
+ { u"measuredangle"_ustr, u"\u2221"_ustr },
+ { u"mfr"_ustr, u"\U0001D52A"_ustr },
+ { u"mho"_ustr, u"\u2127"_ustr },
+ { u"micro"_ustr, u"\u00B5"_ustr },
+ { u"mid"_ustr, u"\u2223"_ustr },
+ { u"midast"_ustr, u"\u002A"_ustr },
+ { u"midcir"_ustr, u"\u2AF0"_ustr },
+ { u"middot"_ustr, u"\u00B7"_ustr },
+ { u"minus"_ustr, u"\u2212"_ustr },
+ { u"minusb"_ustr, u"\u229F"_ustr },
+ { u"minusd"_ustr, u"\u2238"_ustr },
+ { u"minusdu"_ustr, u"\u2A2A"_ustr },
+ { u"mlcp"_ustr, u"\u2ADB"_ustr },
+ { u"mldr"_ustr, u"\u2026"_ustr },
+ { u"mnplus"_ustr, u"\u2213"_ustr },
+ { u"models"_ustr, u"\u22A7"_ustr },
+ { u"mopf"_ustr, u"\U0001D55E"_ustr },
+ { u"mp"_ustr, u"\u2213"_ustr },
+ { u"mscr"_ustr, u"\U0001D4C2"_ustr },
+ { u"mstpos"_ustr, u"\u223E"_ustr },
+ { u"mu"_ustr, u"\u03BC"_ustr },
+ { u"multimap"_ustr, u"\u22B8"_ustr },
+ { u"mumap"_ustr, u"\u22B8"_ustr },
+ { u"nGg"_ustr, u"\u22D9\u0338"_ustr },
+ { u"nGt"_ustr, u"\u226B\u20D2"_ustr },
+ { u"nGtv"_ustr, u"\u226B\u0338"_ustr },
+ { u"nLeftarrow"_ustr, u"\u21CD"_ustr },
+ { u"nLeftrightarrow"_ustr, u"\u21CE"_ustr },
+ { u"nLl"_ustr, u"\u22D8\u0338"_ustr },
+ { u"nLt"_ustr, u"\u226A\u20D2"_ustr },
+ { u"nLtv"_ustr, u"\u226A\u0338"_ustr },
+ { u"nRightarrow"_ustr, u"\u21CF"_ustr },
+ { u"nVDash"_ustr, u"\u22AF"_ustr },
+ { u"nVdash"_ustr, u"\u22AE"_ustr },
+ { u"nabla"_ustr, u"\u2207"_ustr },
+ { u"nacute"_ustr, u"\u0144"_ustr },
+ { u"nang"_ustr, u"\u2220\u20D2"_ustr },
+ { u"nap"_ustr, u"\u2249"_ustr },
+ { u"napE"_ustr, u"\u2A70\u0338"_ustr },
+ { u"napid"_ustr, u"\u224B\u0338"_ustr },
+ { u"napos"_ustr, u"\u0149"_ustr },
+ { u"napprox"_ustr, u"\u2249"_ustr },
+ { u"natur"_ustr, u"\u266E"_ustr },
+ { u"natural"_ustr, u"\u266E"_ustr },
+ { u"naturals"_ustr, u"\u2115"_ustr },
+ { u"nbsp"_ustr, u"\u00A0"_ustr },
+ { u"nbump"_ustr, u"\u224E\u0338"_ustr },
+ { u"nbumpe"_ustr, u"\u224F\u0338"_ustr },
+ { u"ncap"_ustr, u"\u2A43"_ustr },
+ { u"ncaron"_ustr, u"\u0148"_ustr },
+ { u"ncedil"_ustr, u"\u0146"_ustr },
+ { u"ncong"_ustr, u"\u2247"_ustr },
+ { u"ncongdot"_ustr, u"\u2A6D\u0338"_ustr },
+ { u"ncup"_ustr, u"\u2A42"_ustr },
+ { u"ncy"_ustr, u"\u043D"_ustr },
+ { u"ndash"_ustr, u"\u2013"_ustr },
+ { u"ne"_ustr, u"\u2260"_ustr },
+ { u"neArr"_ustr, u"\u21D7"_ustr },
+ { u"nearhk"_ustr, u"\u2924"_ustr },
+ { u"nearr"_ustr, u"\u2197"_ustr },
+ { u"nearrow"_ustr, u"\u2197"_ustr },
+ { u"nedot"_ustr, u"\u2250\u0338"_ustr },
+ { u"nequiv"_ustr, u"\u2262"_ustr },
+ { u"nesear"_ustr, u"\u2928"_ustr },
+ { u"nesim"_ustr, u"\u2242\u0338"_ustr },
+ { u"nexist"_ustr, u"\u2204"_ustr },
+ { u"nexists"_ustr, u"\u2204"_ustr },
+ { u"nfr"_ustr, u"\U0001D52B"_ustr },
+ { u"ngE"_ustr, u"\u2267\u0338"_ustr },
+ { u"nge"_ustr, u"\u2271"_ustr },
+ { u"ngeq"_ustr, u"\u2271"_ustr },
+ { u"ngeqq"_ustr, u"\u2267\u0338"_ustr },
+ { u"ngeqslant"_ustr, u"\u2A7E\u0338"_ustr },
+ { u"nges"_ustr, u"\u2A7E\u0338"_ustr },
+ { u"ngsim"_ustr, u"\u2275"_ustr },
+ { u"ngt"_ustr, u"\u226F"_ustr },
+ { u"ngtr"_ustr, u"\u226F"_ustr },
+ { u"nhArr"_ustr, u"\u21CE"_ustr },
+ { u"nharr"_ustr, u"\u21AE"_ustr },
+ { u"nhpar"_ustr, u"\u2AF2"_ustr },
+ { u"ni"_ustr, u"\u220B"_ustr },
+ { u"nis"_ustr, u"\u22FC"_ustr },
+ { u"nisd"_ustr, u"\u22FA"_ustr },
+ { u"niv"_ustr, u"\u220B"_ustr },
+ { u"njcy"_ustr, u"\u045A"_ustr },
+ { u"nlArr"_ustr, u"\u21CD"_ustr },
+ { u"nlE"_ustr, u"\u2266\u0338"_ustr },
+ { u"nlarr"_ustr, u"\u219A"_ustr },
+ { u"nldr"_ustr, u"\u2025"_ustr },
+ { u"nle"_ustr, u"\u2270"_ustr },
+ { u"nleftarrow"_ustr, u"\u219A"_ustr },
+ { u"nleftrightarrow"_ustr, u"\u21AE"_ustr },
+ { u"nleq"_ustr, u"\u2270"_ustr },
+ { u"nleqq"_ustr, u"\u2266\u0338"_ustr },
+ { u"nleqslant"_ustr, u"\u2A7D\u0338"_ustr },
+ { u"nles"_ustr, u"\u2A7D\u0338"_ustr },
+ { u"nless"_ustr, u"\u226E"_ustr },
+ { u"nlsim"_ustr, u"\u2274"_ustr },
+ { u"nlt"_ustr, u"\u226E"_ustr },
+ { u"nltri"_ustr, u"\u22EA"_ustr },
+ { u"nltrie"_ustr, u"\u22EC"_ustr },
+ { u"nmid"_ustr, u"\u2224"_ustr },
+ { u"nopf"_ustr, u"\U0001D55F"_ustr },
+ { u"not"_ustr, u"\u00AC"_ustr },
+ { u"notin"_ustr, u"\u2209"_ustr },
+ { u"notinE"_ustr, u"\u22F9\u0338"_ustr },
+ { u"notindot"_ustr, u"\u22F5\u0338"_ustr },
+ { u"notinva"_ustr, u"\u2209"_ustr },
+ { u"notinvb"_ustr, u"\u22F7"_ustr },
+ { u"notinvc"_ustr, u"\u22F6"_ustr },
+ { u"notni"_ustr, u"\u220C"_ustr },
+ { u"notniva"_ustr, u"\u220C"_ustr },
+ { u"notnivb"_ustr, u"\u22FE"_ustr },
+ { u"notnivc"_ustr, u"\u22FD"_ustr },
+ { u"npar"_ustr, u"\u2226"_ustr },
+ { u"nparallel"_ustr, u"\u2226"_ustr },
+ { u"nparsl"_ustr, u"\u2AFD\u20E5"_ustr },
+ { u"npart"_ustr, u"\u2202\u0338"_ustr },
+ { u"npolint"_ustr, u"\u2A14"_ustr },
+ { u"npr"_ustr, u"\u2280"_ustr },
+ { u"nprcue"_ustr, u"\u22E0"_ustr },
+ { u"npre"_ustr, u"\u2AAF\u0338"_ustr },
+ { u"nprec"_ustr, u"\u2280"_ustr },
+ { u"npreceq"_ustr, u"\u2AAF\u0338"_ustr },
+ { u"nrArr"_ustr, u"\u21CF"_ustr },
+ { u"nrarr"_ustr, u"\u219B"_ustr },
+ { u"nrarrc"_ustr, u"\u2933\u0338"_ustr },
+ { u"nrarrw"_ustr, u"\u219D\u0338"_ustr },
+ { u"nrightarrow"_ustr, u"\u219B"_ustr },
+ { u"nrtri"_ustr, u"\u22EB"_ustr },
+ { u"nrtrie"_ustr, u"\u22ED"_ustr },
+ { u"nsc"_ustr, u"\u2281"_ustr },
+ { u"nsccue"_ustr, u"\u22E1"_ustr },
+ { u"nsce"_ustr, u"\u2AB0\u0338"_ustr },
+ { u"nscr"_ustr, u"\U0001D4C3"_ustr },
+ { u"nshortmid"_ustr, u"\u2224"_ustr },
+ { u"nshortparallel"_ustr, u"\u2226"_ustr },
+ { u"nsim"_ustr, u"\u2241"_ustr },
+ { u"nsime"_ustr, u"\u2244"_ustr },
+ { u"nsimeq"_ustr, u"\u2244"_ustr },
+ { u"nsmid"_ustr, u"\u2224"_ustr },
+ { u"nspar"_ustr, u"\u2226"_ustr },
+ { u"nsqsube"_ustr, u"\u22E2"_ustr },
+ { u"nsqsupe"_ustr, u"\u22E3"_ustr },
+ { u"nsub"_ustr, u"\u2284"_ustr },
+ { u"nsubE"_ustr, u"\u2AC5\u0338"_ustr },
+ { u"nsube"_ustr, u"\u2288"_ustr },
+ { u"nsubset"_ustr, u"\u2282\u20D2"_ustr },
+ { u"nsubseteq"_ustr, u"\u2288"_ustr },
+ { u"nsubseteqq"_ustr, u"\u2AC5\u0338"_ustr },
+ { u"nsucc"_ustr, u"\u2281"_ustr },
+ { u"nsucceq"_ustr, u"\u2AB0\u0338"_ustr },
+ { u"nsup"_ustr, u"\u2285"_ustr },
+ { u"nsupE"_ustr, u"\u2AC6\u0338"_ustr },
+ { u"nsupe"_ustr, u"\u2289"_ustr },
+ { u"nsupset"_ustr, u"\u2283\u20D2"_ustr },
+ { u"nsupseteq"_ustr, u"\u2289"_ustr },
+ { u"nsupseteqq"_ustr, u"\u2AC6\u0338"_ustr },
+ { u"ntgl"_ustr, u"\u2279"_ustr },
+ { u"ntilde"_ustr, u"\u00F1"_ustr },
+ { u"ntlg"_ustr, u"\u2278"_ustr },
+ { u"ntriangleleft"_ustr, u"\u22EA"_ustr },
+ { u"ntrianglelefteq"_ustr, u"\u22EC"_ustr },
+ { u"ntriangleright"_ustr, u"\u22EB"_ustr },
+ { u"ntrianglerighteq"_ustr, u"\u22ED"_ustr },
+ { u"nu"_ustr, u"\u03BD"_ustr },
+ { u"num"_ustr, u"\u0023"_ustr },
+ { u"numero"_ustr, u"\u2116"_ustr },
+ { u"numsp"_ustr, u"\u2007"_ustr },
+ { u"nvDash"_ustr, u"\u22AD"_ustr },
+ { u"nvHarr"_ustr, u"\u2904"_ustr },
+ { u"nvap"_ustr, u"\u224D\u20D2"_ustr },
+ { u"nvdash"_ustr, u"\u22AC"_ustr },
+ { u"nvge"_ustr, u"\u2265\u20D2"_ustr },
+ { u"nvgt"_ustr, u"\u003E\u20D2"_ustr },
+ { u"nvinfin"_ustr, u"\u29DE"_ustr },
+ { u"nvlArr"_ustr, u"\u2902"_ustr },
+ { u"nvle"_ustr, u"\u2264\u20D2"_ustr },
+ { u"nvlt"_ustr, u"\u003C\u20D2"_ustr },
+ { u"nvltrie"_ustr, u"\u22B4\u20D2"_ustr },
+ { u"nvrArr"_ustr, u"\u2903"_ustr },
+ { u"nvrtrie"_ustr, u"\u22B5\u20D2"_ustr },
+ { u"nvsim"_ustr, u"\u223C\u20D2"_ustr },
+ { u"nwArr"_ustr, u"\u21D6"_ustr },
+ { u"nwarhk"_ustr, u"\u2923"_ustr },
+ { u"nwarr"_ustr, u"\u2196"_ustr },
+ { u"nwarrow"_ustr, u"\u2196"_ustr },
+ { u"nwnear"_ustr, u"\u2927"_ustr },
+ { u"oS"_ustr, u"\u24C8"_ustr },
+ { u"oacute"_ustr, u"\u00F3"_ustr },
+ { u"oast"_ustr, u"\u229B"_ustr },
+ { u"ocir"_ustr, u"\u229A"_ustr },
+ { u"ocirc"_ustr, u"\u00F4"_ustr },
+ { u"ocy"_ustr, u"\u043E"_ustr },
+ { u"odash"_ustr, u"\u229D"_ustr },
+ { u"odblac"_ustr, u"\u0151"_ustr },
+ { u"odiv"_ustr, u"\u2A38"_ustr },
+ { u"odot"_ustr, u"\u2299"_ustr },
+ { u"odsold"_ustr, u"\u29BC"_ustr },
+ { u"oelig"_ustr, u"\u0153"_ustr },
+ { u"ofcir"_ustr, u"\u29BF"_ustr },
+ { u"ofr"_ustr, u"\U0001D52C"_ustr },
+ { u"ogon"_ustr, u"\u02DB"_ustr },
+ { u"ograve"_ustr, u"\u00F2"_ustr },
+ { u"ogt"_ustr, u"\u29C1"_ustr },
+ { u"ohbar"_ustr, u"\u29B5"_ustr },
+ { u"ohm"_ustr, u"\u03A9"_ustr },
+ { u"oint"_ustr, u"\u222E"_ustr },
+ { u"olarr"_ustr, u"\u21BA"_ustr },
+ { u"olcir"_ustr, u"\u29BE"_ustr },
+ { u"olcross"_ustr, u"\u29BB"_ustr },
+ { u"oline"_ustr, u"\u203E"_ustr },
+ { u"olt"_ustr, u"\u29C0"_ustr },
+ { u"omacr"_ustr, u"\u014D"_ustr },
+ { u"omega"_ustr, u"\u03C9"_ustr },
+ { u"omicron"_ustr, u"\u03BF"_ustr },
+ { u"omid"_ustr, u"\u29B6"_ustr },
+ { u"ominus"_ustr, u"\u2296"_ustr },
+ { u"oopf"_ustr, u"\U0001D560"_ustr },
+ { u"opar"_ustr, u"\u29B7"_ustr },
+ { u"operp"_ustr, u"\u29B9"_ustr },
+ { u"oplus"_ustr, u"\u2295"_ustr },
+ { u"or"_ustr, u"\u2228"_ustr },
+ { u"orarr"_ustr, u"\u21BB"_ustr },
+ { u"ord"_ustr, u"\u2A5D"_ustr },
+ { u"order"_ustr, u"\u2134"_ustr },
+ { u"orderof"_ustr, u"\u2134"_ustr },
+ { u"ordf"_ustr, u"\u00AA"_ustr },
+ { u"ordm"_ustr, u"\u00BA"_ustr },
+ { u"origof"_ustr, u"\u22B6"_ustr },
+ { u"oror"_ustr, u"\u2A56"_ustr },
+ { u"orslope"_ustr, u"\u2A57"_ustr },
+ { u"orv"_ustr, u"\u2A5B"_ustr },
+ { u"oscr"_ustr, u"\u2134"_ustr },
+ { u"oslash"_ustr, u"\u00F8"_ustr },
+ { u"osol"_ustr, u"\u2298"_ustr },
+ { u"otilde"_ustr, u"\u00F5"_ustr },
+ { u"otimes"_ustr, u"\u2297"_ustr },
+ { u"otimesas"_ustr, u"\u2A36"_ustr },
+ { u"ouml"_ustr, u"\u00F6"_ustr },
+ { u"ovbar"_ustr, u"\u233D"_ustr },
+ { u"par"_ustr, u"\u2225"_ustr },
+ { u"para"_ustr, u"\u00B6"_ustr },
+ { u"parallel"_ustr, u"\u2225"_ustr },
+ { u"parsim"_ustr, u"\u2AF3"_ustr },
+ { u"parsl"_ustr, u"\u2AFD"_ustr },
+ { u"part"_ustr, u"\u2202"_ustr },
+ { u"pcy"_ustr, u"\u043F"_ustr },
+ { u"percnt"_ustr, u"\u0025"_ustr },
+ { u"period"_ustr, u"\u002E"_ustr },
+ { u"permil"_ustr, u"\u2030"_ustr },
+ { u"perp"_ustr, u"\u22A5"_ustr },
+ { u"pertenk"_ustr, u"\u2031"_ustr },
+ { u"pfr"_ustr, u"\U0001D52D"_ustr },
+ { u"phi"_ustr, u"\u03C6"_ustr },
+ { u"phiv"_ustr, u"\u03D5"_ustr },
+ { u"phmmat"_ustr, u"\u2133"_ustr },
+ { u"phone"_ustr, u"\u260E"_ustr },
+ { u"pi"_ustr, u"\u03C0"_ustr },
+ { u"pitchfork"_ustr, u"\u22D4"_ustr },
+ { u"piv"_ustr, u"\u03D6"_ustr },
+ { u"planck"_ustr, u"\u210F"_ustr },
+ { u"planckh"_ustr, u"\u210E"_ustr },
+ { u"plankv"_ustr, u"\u210F"_ustr },
+ { u"plus"_ustr, u"\u002B"_ustr },
+ { u"plusacir"_ustr, u"\u2A23"_ustr },
+ { u"plusb"_ustr, u"\u229E"_ustr },
+ { u"pluscir"_ustr, u"\u2A22"_ustr },
+ { u"plusdo"_ustr, u"\u2214"_ustr },
+ { u"plusdu"_ustr, u"\u2A25"_ustr },
+ { u"pluse"_ustr, u"\u2A72"_ustr },
+ { u"plusmn"_ustr, u"\u00B1"_ustr },
+ { u"plussim"_ustr, u"\u2A26"_ustr },
+ { u"plustwo"_ustr, u"\u2A27"_ustr },
+ { u"pm"_ustr, u"\u00B1"_ustr },
+ { u"pointint"_ustr, u"\u2A15"_ustr },
+ { u"popf"_ustr, u"\U0001D561"_ustr },
+ { u"pound"_ustr, u"\u00A3"_ustr },
+ { u"pr"_ustr, u"\u227A"_ustr },
+ { u"prE"_ustr, u"\u2AB3"_ustr },
+ { u"prap"_ustr, u"\u2AB7"_ustr },
+ { u"prcue"_ustr, u"\u227C"_ustr },
+ { u"pre"_ustr, u"\u2AAF"_ustr },
+ { u"prec"_ustr, u"\u227A"_ustr },
+ { u"precapprox"_ustr, u"\u2AB7"_ustr },
+ { u"preccurlyeq"_ustr, u"\u227C"_ustr },
+ { u"preceq"_ustr, u"\u2AAF"_ustr },
+ { u"precnapprox"_ustr, u"\u2AB9"_ustr },
+ { u"precneqq"_ustr, u"\u2AB5"_ustr },
+ { u"precnsim"_ustr, u"\u22E8"_ustr },
+ { u"precsim"_ustr, u"\u227E"_ustr },
+ { u"prime"_ustr, u"\u2032"_ustr },
+ { u"primes"_ustr, u"\u2119"_ustr },
+ { u"prnE"_ustr, u"\u2AB5"_ustr },
+ { u"prnap"_ustr, u"\u2AB9"_ustr },
+ { u"prnsim"_ustr, u"\u22E8"_ustr },
+ { u"prod"_ustr, u"\u220F"_ustr },
+ { u"profalar"_ustr, u"\u232E"_ustr },
+ { u"profline"_ustr, u"\u2312"_ustr },
+ { u"profsurf"_ustr, u"\u2313"_ustr },
+ { u"prop"_ustr, u"\u221D"_ustr },
+ { u"propto"_ustr, u"\u221D"_ustr },
+ { u"prsim"_ustr, u"\u227E"_ustr },
+ { u"prurel"_ustr, u"\u22B0"_ustr },
+ { u"pscr"_ustr, u"\U0001D4C5"_ustr },
+ { u"psi"_ustr, u"\u03C8"_ustr },
+ { u"puncsp"_ustr, u"\u2008"_ustr },
+ { u"qfr"_ustr, u"\U0001D52E"_ustr },
+ { u"qint"_ustr, u"\u2A0C"_ustr },
+ { u"qopf"_ustr, u"\U0001D562"_ustr },
+ { u"qprime"_ustr, u"\u2057"_ustr },
+ { u"qscr"_ustr, u"\U0001D4C6"_ustr },
+ { u"quaternions"_ustr, u"\u210D"_ustr },
+ { u"quatint"_ustr, u"\u2A16"_ustr },
+ { u"quest"_ustr, u"\u003F"_ustr },
+ { u"questeq"_ustr, u"\u225F"_ustr },
+ { u"quot"_ustr, u"\u0022"_ustr },
+ { u"rAarr"_ustr, u"\u21DB"_ustr },
+ { u"rArr"_ustr, u"\u21D2"_ustr },
+ { u"rAtail"_ustr, u"\u291C"_ustr },
+ { u"rBarr"_ustr, u"\u290F"_ustr },
+ { u"rHar"_ustr, u"\u2964"_ustr },
+ { u"race"_ustr, u"\u223D\u0331"_ustr },
+ { u"racute"_ustr, u"\u0155"_ustr },
+ { u"radic"_ustr, u"\u221A"_ustr },
+ { u"raemptyv"_ustr, u"\u29B3"_ustr },
+ { u"rang"_ustr, u"\u27E9"_ustr },
+ { u"rangd"_ustr, u"\u2992"_ustr },
+ { u"range"_ustr, u"\u29A5"_ustr },
+ { u"rangle"_ustr, u"\u27E9"_ustr },
+ { u"raquo"_ustr, u"\u00BB"_ustr },
+ { u"rarr"_ustr, u"\u2192"_ustr },
+ { u"rarrap"_ustr, u"\u2975"_ustr },
+ { u"rarrb"_ustr, u"\u21E5"_ustr },
+ { u"rarrbfs"_ustr, u"\u2920"_ustr },
+ { u"rarrc"_ustr, u"\u2933"_ustr },
+ { u"rarrfs"_ustr, u"\u291E"_ustr },
+ { u"rarrhk"_ustr, u"\u21AA"_ustr },
+ { u"rarrlp"_ustr, u"\u21AC"_ustr },
+ { u"rarrpl"_ustr, u"\u2945"_ustr },
+ { u"rarrsim"_ustr, u"\u2974"_ustr },
+ { u"rarrtl"_ustr, u"\u21A3"_ustr },
+ { u"rarrw"_ustr, u"\u219D"_ustr },
+ { u"ratail"_ustr, u"\u291A"_ustr },
+ { u"ratio"_ustr, u"\u2236"_ustr },
+ { u"rationals"_ustr, u"\u211A"_ustr },
+ { u"rbarr"_ustr, u"\u290D"_ustr },
+ { u"rbbrk"_ustr, u"\u2773"_ustr },
+ { u"rbrace"_ustr, u"\u007D"_ustr },
+ { u"rbrack"_ustr, u"\u005D"_ustr },
+ { u"rbrke"_ustr, u"\u298C"_ustr },
+ { u"rbrksld"_ustr, u"\u298E"_ustr },
+ { u"rbrkslu"_ustr, u"\u2990"_ustr },
+ { u"rcaron"_ustr, u"\u0159"_ustr },
+ { u"rcedil"_ustr, u"\u0157"_ustr },
+ { u"rceil"_ustr, u"\u2309"_ustr },
+ { u"rcub"_ustr, u"\u007D"_ustr },
+ { u"rcy"_ustr, u"\u0440"_ustr },
+ { u"rdca"_ustr, u"\u2937"_ustr },
+ { u"rdldhar"_ustr, u"\u2969"_ustr },
+ { u"rdquo"_ustr, u"\u201D"_ustr },
+ { u"rdquor"_ustr, u"\u201D"_ustr },
+ { u"rdsh"_ustr, u"\u21B3"_ustr },
+ { u"real"_ustr, u"\u211C"_ustr },
+ { u"realine"_ustr, u"\u211B"_ustr },
+ { u"realpart"_ustr, u"\u211C"_ustr },
+ { u"reals"_ustr, u"\u211D"_ustr },
+ { u"rect"_ustr, u"\u25AD"_ustr },
+ { u"reg"_ustr, u"\u00AE"_ustr },
+ { u"rfisht"_ustr, u"\u297D"_ustr },
+ { u"rfloor"_ustr, u"\u230B"_ustr },
+ { u"rfr"_ustr, u"\U0001D52F"_ustr },
+ { u"rhard"_ustr, u"\u21C1"_ustr },
+ { u"rharu"_ustr, u"\u21C0"_ustr },
+ { u"rharul"_ustr, u"\u296C"_ustr },
+ { u"rho"_ustr, u"\u03C1"_ustr },
+ { u"rhov"_ustr, u"\u03F1"_ustr },
+ { u"rightarrow"_ustr, u"\u2192"_ustr },
+ { u"rightarrowtail"_ustr, u"\u21A3"_ustr },
+ { u"rightharpoondown"_ustr, u"\u21C1"_ustr },
+ { u"rightharpoonup"_ustr, u"\u21C0"_ustr },
+ { u"rightleftarrows"_ustr, u"\u21C4"_ustr },
+ { u"rightleftharpoons"_ustr, u"\u21CC"_ustr },
+ { u"rightrightarrows"_ustr, u"\u21C9"_ustr },
+ { u"rightsquigarrow"_ustr, u"\u219D"_ustr },
+ { u"rightthreetimes"_ustr, u"\u22CC"_ustr },
+ { u"ring"_ustr, u"\u02DA"_ustr },
+ { u"risingdotseq"_ustr, u"\u2253"_ustr },
+ { u"rlarr"_ustr, u"\u21C4"_ustr },
+ { u"rlhar"_ustr, u"\u21CC"_ustr },
+ { u"rlm"_ustr, u"\u200F"_ustr },
+ { u"rmoust"_ustr, u"\u23B1"_ustr },
+ { u"rmoustache"_ustr, u"\u23B1"_ustr },
+ { u"rnmid"_ustr, u"\u2AEE"_ustr },
+ { u"roang"_ustr, u"\u27ED"_ustr },
+ { u"roarr"_ustr, u"\u21FE"_ustr },
+ { u"robrk"_ustr, u"\u27E7"_ustr },
+ { u"ropar"_ustr, u"\u2986"_ustr },
+ { u"ropf"_ustr, u"\U0001D563"_ustr },
+ { u"roplus"_ustr, u"\u2A2E"_ustr },
+ { u"rotimes"_ustr, u"\u2A35"_ustr },
+ { u"rpar"_ustr, u"\u0029"_ustr },
+ { u"rpargt"_ustr, u"\u2994"_ustr },
+ { u"rppolint"_ustr, u"\u2A12"_ustr },
+ { u"rrarr"_ustr, u"\u21C9"_ustr },
+ { u"rsaquo"_ustr, u"\u203A"_ustr },
+ { u"rscr"_ustr, u"\U0001D4C7"_ustr },
+ { u"rsh"_ustr, u"\u21B1"_ustr },
+ { u"rsqb"_ustr, u"\u005D"_ustr },
+ { u"rsquo"_ustr, u"\u2019"_ustr },
+ { u"rsquor"_ustr, u"\u2019"_ustr },
+ { u"rthree"_ustr, u"\u22CC"_ustr },
+ { u"rtimes"_ustr, u"\u22CA"_ustr },
+ { u"rtri"_ustr, u"\u25B9"_ustr },
+ { u"rtrie"_ustr, u"\u22B5"_ustr },
+ { u"rtrif"_ustr, u"\u25B8"_ustr },
+ { u"rtriltri"_ustr, u"\u29CE"_ustr },
+ { u"ruluhar"_ustr, u"\u2968"_ustr },
+ { u"rx"_ustr, u"\u211E"_ustr },
+ { u"sacute"_ustr, u"\u015B"_ustr },
+ { u"sbquo"_ustr, u"\u201A"_ustr },
+ { u"sc"_ustr, u"\u227B"_ustr },
+ { u"scE"_ustr, u"\u2AB4"_ustr },
+ { u"scap"_ustr, u"\u2AB8"_ustr },
+ { u"scaron"_ustr, u"\u0161"_ustr },
+ { u"sccue"_ustr, u"\u227D"_ustr },
+ { u"sce"_ustr, u"\u2AB0"_ustr },
+ { u"scedil"_ustr, u"\u015F"_ustr },
+ { u"scirc"_ustr, u"\u015D"_ustr },
+ { u"scnE"_ustr, u"\u2AB6"_ustr },
+ { u"scnap"_ustr, u"\u2ABA"_ustr },
+ { u"scnsim"_ustr, u"\u22E9"_ustr },
+ { u"scpolint"_ustr, u"\u2A13"_ustr },
+ { u"scsim"_ustr, u"\u227F"_ustr },
+ { u"scy"_ustr, u"\u0441"_ustr },
+ { u"sdot"_ustr, u"\u22C5"_ustr },
+ { u"sdotb"_ustr, u"\u22A1"_ustr },
+ { u"sdote"_ustr, u"\u2A66"_ustr },
+ { u"seArr"_ustr, u"\u21D8"_ustr },
+ { u"searhk"_ustr, u"\u2925"_ustr },
+ { u"searr"_ustr, u"\u2198"_ustr },
+ { u"searrow"_ustr, u"\u2198"_ustr },
+ { u"sect"_ustr, u"\u00A7"_ustr },
+ { u"semi"_ustr, u"\u003B"_ustr },
+ { u"seswar"_ustr, u"\u2929"_ustr },
+ { u"setminus"_ustr, u"\u2216"_ustr },
+ { u"setmn"_ustr, u"\u2216"_ustr },
+ { u"sext"_ustr, u"\u2736"_ustr },
+ { u"sfr"_ustr, u"\U0001D530"_ustr },
+ { u"sfrown"_ustr, u"\u2322"_ustr },
+ { u"sharp"_ustr, u"\u266F"_ustr },
+ { u"shchcy"_ustr, u"\u0449"_ustr },
+ { u"shcy"_ustr, u"\u0448"_ustr },
+ { u"shortmid"_ustr, u"\u2223"_ustr },
+ { u"shortparallel"_ustr, u"\u2225"_ustr },
+ { u"shy"_ustr, u"\u00AD"_ustr },
+ { u"sigma"_ustr, u"\u03C3"_ustr },
+ { u"sigmaf"_ustr, u"\u03C2"_ustr },
+ { u"sigmav"_ustr, u"\u03C2"_ustr },
+ { u"sim"_ustr, u"\u223C"_ustr },
+ { u"simdot"_ustr, u"\u2A6A"_ustr },
+ { u"sime"_ustr, u"\u2243"_ustr },
+ { u"simeq"_ustr, u"\u2243"_ustr },
+ { u"simg"_ustr, u"\u2A9E"_ustr },
+ { u"simgE"_ustr, u"\u2AA0"_ustr },
+ { u"siml"_ustr, u"\u2A9D"_ustr },
+ { u"simlE"_ustr, u"\u2A9F"_ustr },
+ { u"simne"_ustr, u"\u2246"_ustr },
+ { u"simplus"_ustr, u"\u2A24"_ustr },
+ { u"simrarr"_ustr, u"\u2972"_ustr },
+ { u"slarr"_ustr, u"\u2190"_ustr },
+ { u"smallsetminus"_ustr, u"\u2216"_ustr },
+ { u"smashp"_ustr, u"\u2A33"_ustr },
+ { u"smeparsl"_ustr, u"\u29E4"_ustr },
+ { u"smid"_ustr, u"\u2223"_ustr },
+ { u"smile"_ustr, u"\u2323"_ustr },
+ { u"smt"_ustr, u"\u2AAA"_ustr },
+ { u"smte"_ustr, u"\u2AAC"_ustr },
+ { u"smtes"_ustr, u"\u2AAC\uFE00"_ustr },
+ { u"softcy"_ustr, u"\u044C"_ustr },
+ { u"sol"_ustr, u"\u002F"_ustr },
+ { u"solb"_ustr, u"\u29C4"_ustr },
+ { u"solbar"_ustr, u"\u233F"_ustr },
+ { u"sopf"_ustr, u"\U0001D564"_ustr },
+ { u"spades"_ustr, u"\u2660"_ustr },
+ { u"spadesuit"_ustr, u"\u2660"_ustr },
+ { u"spar"_ustr, u"\u2225"_ustr },
+ { u"sqcap"_ustr, u"\u2293"_ustr },
+ { u"sqcaps"_ustr, u"\u2293\uFE00"_ustr },
+ { u"sqcup"_ustr, u"\u2294"_ustr },
+ { u"sqcups"_ustr, u"\u2294\uFE00"_ustr },
+ { u"sqsub"_ustr, u"\u228F"_ustr },
+ { u"sqsube"_ustr, u"\u2291"_ustr },
+ { u"sqsubset"_ustr, u"\u228F"_ustr },
+ { u"sqsubseteq"_ustr, u"\u2291"_ustr },
+ { u"sqsup"_ustr, u"\u2290"_ustr },
+ { u"sqsupe"_ustr, u"\u2292"_ustr },
+ { u"sqsupset"_ustr, u"\u2290"_ustr },
+ { u"sqsupseteq"_ustr, u"\u2292"_ustr },
+ { u"squ"_ustr, u"\u25A1"_ustr },
+ { u"square"_ustr, u"\u25A1"_ustr },
+ { u"squarf"_ustr, u"\u25AA"_ustr },
+ { u"squf"_ustr, u"\u25AA"_ustr },
+ { u"srarr"_ustr, u"\u2192"_ustr },
+ { u"sscr"_ustr, u"\U0001D4C8"_ustr },
+ { u"ssetmn"_ustr, u"\u2216"_ustr },
+ { u"ssmile"_ustr, u"\u2323"_ustr },
+ { u"sstarf"_ustr, u"\u22C6"_ustr },
+ { u"star"_ustr, u"\u2606"_ustr },
+ { u"starf"_ustr, u"\u2605"_ustr },
+ { u"straightepsilon"_ustr, u"\u03F5"_ustr },
+ { u"straightphi"_ustr, u"\u03D5"_ustr },
+ { u"strns"_ustr, u"\u00AF"_ustr },
+ { u"sub"_ustr, u"\u2282"_ustr },
+ { u"subE"_ustr, u"\u2AC5"_ustr },
+ { u"subdot"_ustr, u"\u2ABD"_ustr },
+ { u"sube"_ustr, u"\u2286"_ustr },
+ { u"subedot"_ustr, u"\u2AC3"_ustr },
+ { u"submult"_ustr, u"\u2AC1"_ustr },
+ { u"subnE"_ustr, u"\u2ACB"_ustr },
+ { u"subne"_ustr, u"\u228A"_ustr },
+ { u"subplus"_ustr, u"\u2ABF"_ustr },
+ { u"subrarr"_ustr, u"\u2979"_ustr },
+ { u"subset"_ustr, u"\u2282"_ustr },
+ { u"subseteq"_ustr, u"\u2286"_ustr },
+ { u"subseteqq"_ustr, u"\u2AC5"_ustr },
+ { u"subsetneq"_ustr, u"\u228A"_ustr },
+ { u"subsetneqq"_ustr, u"\u2ACB"_ustr },
+ { u"subsim"_ustr, u"\u2AC7"_ustr },
+ { u"subsub"_ustr, u"\u2AD5"_ustr },
+ { u"subsup"_ustr, u"\u2AD3"_ustr },
+ { u"succ"_ustr, u"\u227B"_ustr },
+ { u"succapprox"_ustr, u"\u2AB8"_ustr },
+ { u"succcurlyeq"_ustr, u"\u227D"_ustr },
+ { u"succeq"_ustr, u"\u2AB0"_ustr },
+ { u"succnapprox"_ustr, u"\u2ABA"_ustr },
+ { u"succneqq"_ustr, u"\u2AB6"_ustr },
+ { u"succnsim"_ustr, u"\u22E9"_ustr },
+ { u"succsim"_ustr, u"\u227F"_ustr },
+ { u"sum"_ustr, u"\u2211"_ustr },
+ { u"sung"_ustr, u"\u266A"_ustr },
+ { u"sup"_ustr, u"\u2283"_ustr },
+ { u"sup1"_ustr, u"\u00B9"_ustr },
+ { u"sup2"_ustr, u"\u00B2"_ustr },
+ { u"sup3"_ustr, u"\u00B3"_ustr },
+ { u"supE"_ustr, u"\u2AC6"_ustr },
+ { u"supdot"_ustr, u"\u2ABE"_ustr },
+ { u"supdsub"_ustr, u"\u2AD8"_ustr },
+ { u"supe"_ustr, u"\u2287"_ustr },
+ { u"supedot"_ustr, u"\u2AC4"_ustr },
+ { u"suphsol"_ustr, u"\u27C9"_ustr },
+ { u"suphsub"_ustr, u"\u2AD7"_ustr },
+ { u"suplarr"_ustr, u"\u297B"_ustr },
+ { u"supmult"_ustr, u"\u2AC2"_ustr },
+ { u"supnE"_ustr, u"\u2ACC"_ustr },
+ { u"supne"_ustr, u"\u228B"_ustr },
+ { u"supplus"_ustr, u"\u2AC0"_ustr },
+ { u"supset"_ustr, u"\u2283"_ustr },
+ { u"supseteq"_ustr, u"\u2287"_ustr },
+ { u"supseteqq"_ustr, u"\u2AC6"_ustr },
+ { u"supsetneq"_ustr, u"\u228B"_ustr },
+ { u"supsetneqq"_ustr, u"\u2ACC"_ustr },
+ { u"supsim"_ustr, u"\u2AC8"_ustr },
+ { u"supsub"_ustr, u"\u2AD4"_ustr },
+ { u"supsup"_ustr, u"\u2AD6"_ustr },
+ { u"swArr"_ustr, u"\u21D9"_ustr },
+ { u"swarhk"_ustr, u"\u2926"_ustr },
+ { u"swarr"_ustr, u"\u2199"_ustr },
+ { u"swarrow"_ustr, u"\u2199"_ustr },
+ { u"swnwar"_ustr, u"\u292A"_ustr },
+ { u"szlig"_ustr, u"\u00DF"_ustr },
+ { u"target"_ustr, u"\u2316"_ustr },
+ { u"tau"_ustr, u"\u03C4"_ustr },
+ { u"tbrk"_ustr, u"\u23B4"_ustr },
+ { u"tcaron"_ustr, u"\u0165"_ustr },
+ { u"tcedil"_ustr, u"\u0163"_ustr },
+ { u"tcy"_ustr, u"\u0442"_ustr },
+ { u"tdot"_ustr, u"\u20DB"_ustr },
+ { u"telrec"_ustr, u"\u2315"_ustr },
+ { u"tfr"_ustr, u"\U0001D531"_ustr },
+ { u"there4"_ustr, u"\u2234"_ustr },
+ { u"therefore"_ustr, u"\u2234"_ustr },
+ { u"theta"_ustr, u"\u03B8"_ustr },
+ { u"thetasym"_ustr, u"\u03D1"_ustr },
+ { u"thetav"_ustr, u"\u03D1"_ustr },
+ { u"thickapprox"_ustr, u"\u2248"_ustr },
+ { u"thicksim"_ustr, u"\u223C"_ustr },
+ { u"thinsp"_ustr, u"\u2009"_ustr },
+ { u"thkap"_ustr, u"\u2248"_ustr },
+ { u"thksim"_ustr, u"\u223C"_ustr },
+ { u"thorn"_ustr, u"\u00FE"_ustr },
+ { u"tilde"_ustr, u"\u02DC"_ustr },
+ { u"times"_ustr, u"\u00D7"_ustr },
+ { u"timesb"_ustr, u"\u22A0"_ustr },
+ { u"timesbar"_ustr, u"\u2A31"_ustr },
+ { u"timesd"_ustr, u"\u2A30"_ustr },
+ { u"tint"_ustr, u"\u222D"_ustr },
+ { u"toea"_ustr, u"\u2928"_ustr },
+ { u"top"_ustr, u"\u22A4"_ustr },
+ { u"topbot"_ustr, u"\u2336"_ustr },
+ { u"topcir"_ustr, u"\u2AF1"_ustr },
+ { u"topf"_ustr, u"\U0001D565"_ustr },
+ { u"topfork"_ustr, u"\u2ADA"_ustr },
+ { u"tosa"_ustr, u"\u2929"_ustr },
+ { u"tprime"_ustr, u"\u2034"_ustr },
+ { u"trade"_ustr, u"\u2122"_ustr },
+ { u"triangle"_ustr, u"\u25B5"_ustr },
+ { u"triangledown"_ustr, u"\u25BF"_ustr },
+ { u"triangleleft"_ustr, u"\u25C3"_ustr },
+ { u"trianglelefteq"_ustr, u"\u22B4"_ustr },
+ { u"triangleq"_ustr, u"\u225C"_ustr },
+ { u"triangleright"_ustr, u"\u25B9"_ustr },
+ { u"trianglerighteq"_ustr, u"\u22B5"_ustr },
+ { u"tridot"_ustr, u"\u25EC"_ustr },
+ { u"trie"_ustr, u"\u225C"_ustr },
+ { u"triminus"_ustr, u"\u2A3A"_ustr },
+ { u"triplus"_ustr, u"\u2A39"_ustr },
+ { u"trisb"_ustr, u"\u29CD"_ustr },
+ { u"tritime"_ustr, u"\u2A3B"_ustr },
+ { u"trpezium"_ustr, u"\u23E2"_ustr },
+ { u"tscr"_ustr, u"\U0001D4C9"_ustr },
+ { u"tscy"_ustr, u"\u0446"_ustr },
+ { u"tshcy"_ustr, u"\u045B"_ustr },
+ { u"tstrok"_ustr, u"\u0167"_ustr },
+ { u"twixt"_ustr, u"\u226C"_ustr },
+ { u"twoheadleftarrow"_ustr, u"\u219E"_ustr },
+ { u"twoheadrightarrow"_ustr, u"\u21A0"_ustr },
+ { u"uArr"_ustr, u"\u21D1"_ustr },
+ { u"uHar"_ustr, u"\u2963"_ustr },
+ { u"uacute"_ustr, u"\u00FA"_ustr },
+ { u"uarr"_ustr, u"\u2191"_ustr },
+ { u"ubrcy"_ustr, u"\u045E"_ustr },
+ { u"ubreve"_ustr, u"\u016D"_ustr },
+ { u"ucirc"_ustr, u"\u00FB"_ustr },
+ { u"ucy"_ustr, u"\u0443"_ustr },
+ { u"udarr"_ustr, u"\u21C5"_ustr },
+ { u"udblac"_ustr, u"\u0171"_ustr },
+ { u"udhar"_ustr, u"\u296E"_ustr },
+ { u"ufisht"_ustr, u"\u297E"_ustr },
+ { u"ufr"_ustr, u"\U0001D532"_ustr },
+ { u"ugrave"_ustr, u"\u00F9"_ustr },
+ { u"uharl"_ustr, u"\u21BF"_ustr },
+ { u"uharr"_ustr, u"\u21BE"_ustr },
+ { u"uhblk"_ustr, u"\u2580"_ustr },
+ { u"ulcorn"_ustr, u"\u231C"_ustr },
+ { u"ulcorner"_ustr, u"\u231C"_ustr },
+ { u"ulcrop"_ustr, u"\u230F"_ustr },
+ { u"ultri"_ustr, u"\u25F8"_ustr },
+ { u"umacr"_ustr, u"\u016B"_ustr },
+ { u"uml"_ustr, u"\u00A8"_ustr },
+ { u"uogon"_ustr, u"\u0173"_ustr },
+ { u"uopf"_ustr, u"\U0001D566"_ustr },
+ { u"uparrow"_ustr, u"\u2191"_ustr },
+ { u"updownarrow"_ustr, u"\u2195"_ustr },
+ { u"upharpoonleft"_ustr, u"\u21BF"_ustr },
+ { u"upharpoonright"_ustr, u"\u21BE"_ustr },
+ { u"uplus"_ustr, u"\u228E"_ustr },
+ { u"upsi"_ustr, u"\u03C5"_ustr },
+ { u"upsih"_ustr, u"\u03D2"_ustr },
+ { u"upsilon"_ustr, u"\u03C5"_ustr },
+ { u"upuparrows"_ustr, u"\u21C8"_ustr },
+ { u"urcorn"_ustr, u"\u231D"_ustr },
+ { u"urcorner"_ustr, u"\u231D"_ustr },
+ { u"urcrop"_ustr, u"\u230E"_ustr },
+ { u"uring"_ustr, u"\u016F"_ustr },
+ { u"urtri"_ustr, u"\u25F9"_ustr },
+ { u"uscr"_ustr, u"\U0001D4CA"_ustr },
+ { u"utdot"_ustr, u"\u22F0"_ustr },
+ { u"utilde"_ustr, u"\u0169"_ustr },
+ { u"utri"_ustr, u"\u25B5"_ustr },
+ { u"utrif"_ustr, u"\u25B4"_ustr },
+ { u"uuarr"_ustr, u"\u21C8"_ustr },
+ { u"uuml"_ustr, u"\u00FC"_ustr },
+ { u"uwangle"_ustr, u"\u29A7"_ustr },
+ { u"vArr"_ustr, u"\u21D5"_ustr },
+ { u"vBar"_ustr, u"\u2AE8"_ustr },
+ { u"vBarv"_ustr, u"\u2AE9"_ustr },
+ { u"vDash"_ustr, u"\u22A8"_ustr },
+ { u"vangrt"_ustr, u"\u299C"_ustr },
+ { u"varepsilon"_ustr, u"\u03F5"_ustr },
+ { u"varkappa"_ustr, u"\u03F0"_ustr },
+ { u"varnothing"_ustr, u"\u2205"_ustr },
+ { u"varphi"_ustr, u"\u03D5"_ustr },
+ { u"varpi"_ustr, u"\u03D6"_ustr },
+ { u"varpropto"_ustr, u"\u221D"_ustr },
+ { u"varr"_ustr, u"\u2195"_ustr },
+ { u"varrho"_ustr, u"\u03F1"_ustr },
+ { u"varsigma"_ustr, u"\u03C2"_ustr },
+ { u"varsubsetneq"_ustr, u"\u228A\uFE00"_ustr },
+ { u"varsubsetneqq"_ustr, u"\u2ACB\uFE00"_ustr },
+ { u"varsupsetneq"_ustr, u"\u228B\uFE00"_ustr },
+ { u"varsupsetneqq"_ustr, u"\u2ACC\uFE00"_ustr },
+ { u"vartheta"_ustr, u"\u03D1"_ustr },
+ { u"vartriangleleft"_ustr, u"\u22B2"_ustr },
+ { u"vartriangleright"_ustr, u"\u22B3"_ustr },
+ { u"vcy"_ustr, u"\u0432"_ustr },
+ { u"vdash"_ustr, u"\u22A2"_ustr },
+ { u"vee"_ustr, u"\u2228"_ustr },
+ { u"veebar"_ustr, u"\u22BB"_ustr },
+ { u"veeeq"_ustr, u"\u225A"_ustr },
+ { u"vellip"_ustr, u"\u22EE"_ustr },
+ { u"verbar"_ustr, u"\u007C"_ustr },
+ { u"vert"_ustr, u"\u007C"_ustr },
+ { u"vfr"_ustr, u"\U0001D533"_ustr },
+ { u"vltri"_ustr, u"\u22B2"_ustr },
+ { u"vnsub"_ustr, u"\u2282\u20D2"_ustr },
+ { u"vnsup"_ustr, u"\u2283\u20D2"_ustr },
+ { u"vopf"_ustr, u"\U0001D567"_ustr },
+ { u"vprop"_ustr, u"\u221D"_ustr },
+ { u"vrtri"_ustr, u"\u22B3"_ustr },
+ { u"vscr"_ustr, u"\U0001D4CB"_ustr },
+ { u"vsubnE"_ustr, u"\u2ACB\uFE00"_ustr },
+ { u"vsubne"_ustr, u"\u228A\uFE00"_ustr },
+ { u"vsupnE"_ustr, u"\u2ACC\uFE00"_ustr },
+ { u"vsupne"_ustr, u"\u228B\uFE00"_ustr },
+ { u"vzigzag"_ustr, u"\u299A"_ustr },
+ { u"wcirc"_ustr, u"\u0175"_ustr },
+ { u"wedbar"_ustr, u"\u2A5F"_ustr },
+ { u"wedge"_ustr, u"\u2227"_ustr },
+ { u"wedgeq"_ustr, u"\u2259"_ustr },
+ { u"weierp"_ustr, u"\u2118"_ustr },
+ { u"wfr"_ustr, u"\U0001D534"_ustr },
+ { u"wopf"_ustr, u"\U0001D568"_ustr },
+ { u"wp"_ustr, u"\u2118"_ustr },
+ { u"wr"_ustr, u"\u2240"_ustr },
+ { u"wreath"_ustr, u"\u2240"_ustr },
+ { u"wscr"_ustr, u"\U0001D4CC"_ustr },
+ { u"xcap"_ustr, u"\u22C2"_ustr },
+ { u"xcirc"_ustr, u"\u25EF"_ustr },
+ { u"xcup"_ustr, u"\u22C3"_ustr },
+ { u"xdtri"_ustr, u"\u25BD"_ustr },
+ { u"xfr"_ustr, u"\U0001D535"_ustr },
+ { u"xhArr"_ustr, u"\u27FA"_ustr },
+ { u"xharr"_ustr, u"\u27F7"_ustr },
+ { u"xi"_ustr, u"\u03BE"_ustr },
+ { u"xlArr"_ustr, u"\u27F8"_ustr },
+ { u"xlarr"_ustr, u"\u27F5"_ustr },
+ { u"xmap"_ustr, u"\u27FC"_ustr },
+ { u"xnis"_ustr, u"\u22FB"_ustr },
+ { u"xodot"_ustr, u"\u2A00"_ustr },
+ { u"xopf"_ustr, u"\U0001D569"_ustr },
+ { u"xoplus"_ustr, u"\u2A01"_ustr },
+ { u"xotime"_ustr, u"\u2A02"_ustr },
+ { u"xrArr"_ustr, u"\u27F9"_ustr },
+ { u"xrarr"_ustr, u"\u27F6"_ustr },
+ { u"xscr"_ustr, u"\U0001D4CD"_ustr },
+ { u"xsqcup"_ustr, u"\u2A06"_ustr },
+ { u"xuplus"_ustr, u"\u2A04"_ustr },
+ { u"xutri"_ustr, u"\u25B3"_ustr },
+ { u"xvee"_ustr, u"\u22C1"_ustr },
+ { u"xwedge"_ustr, u"\u22C0"_ustr },
+ { u"yacute"_ustr, u"\u00FD"_ustr },
+ { u"yacy"_ustr, u"\u044F"_ustr },
+ { u"ycirc"_ustr, u"\u0177"_ustr },
+ { u"ycy"_ustr, u"\u044B"_ustr },
+ { u"yen"_ustr, u"\u00A5"_ustr },
+ { u"yfr"_ustr, u"\U0001D536"_ustr },
+ { u"yicy"_ustr, u"\u0457"_ustr },
+ { u"yopf"_ustr, u"\U0001D56A"_ustr },
+ { u"yscr"_ustr, u"\U0001D4CE"_ustr },
+ { u"yucy"_ustr, u"\u044E"_ustr },
+ { u"yuml"_ustr, u"\u00FF"_ustr },
+ { u"zacute"_ustr, u"\u017A"_ustr },
+ { u"zcaron"_ustr, u"\u017E"_ustr },
+ { u"zcy"_ustr, u"\u0437"_ustr },
+ { u"zdot"_ustr, u"\u017C"_ustr },
+ { u"zeetrf"_ustr, u"\u2128"_ustr },
+ { u"zeta"_ustr, u"\u03B6"_ustr },
+ { u"zfr"_ustr, u"\U0001D537"_ustr },
+ { u"zhcy"_ustr, u"\u0436"_ustr },
+ { u"zigrarr"_ustr, u"\u21DD"_ustr },
+ { u"zopf"_ustr, u"\U0001D56B"_ustr },
+ { u"zscr"_ustr, u"\U0001D4CF"_ustr },
+ { u"zwj"_ustr, u"\u200D"_ustr },
+ { u"zwnj"_ustr, u"\u200C"_ustr }
+ // 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;"_ustr, u"\u03C3"_ustr},
+ { u"&infin;"_ustr, u"\u221E"_ustr}
+ // 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 0000000000..aca49f034a
--- /dev/null
+++ b/starmath/source/mathtype.cxx
@@ -0,0 +1,3347 @@
+/* -*- 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);
+ 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_uInt64 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::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;
+ case SmNodeType::Expression: // same treatment as the default one
+ 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);
+ 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);
+ 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);
+ 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 ||
+ pIsText->GetToken().eType != TTEXT ||
+ pIsText->GetText().getLength() > 1)
+ nOldPending = StartTemplate(0x11);
+ break;
+ default:
+ nPendingAttributes++;
+ break;
+ }
+ }
+
+ if (pIsText)
+ HandleNodes(pIsText,nLevel+1);
+
+ if (pTemp)
+ {
+ switch (pTemp->GetToken().eType)
+ {
+ case TWIDEVEC:
+ case TUNDERLINE:
+ EndTemplate(nOldPending);
+ break;
+ case TOVERLINE:
+ if (!pIsText ||
+ 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 &&
+ (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 0000000000..cd06cfcddf
--- /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 0000000000..778baaa74d
--- /dev/null
+++ b/starmath/source/node.cxx
@@ -0,0 +1,2491 @@
+/* -*- 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 <utility>
+#include <visitors.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/metric.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/numeric/ftools.hxx>
+#include <unicode/uchar.h>
+#include <unicode/uscript.h>
+
+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, SmToken aNodeToken)
+ : maNodeToken(std::move( aNodeToken ))
+ , 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(conversionFract(o3tl::Length::pt, SmO3tlLengthUnit()) * rSize);
+ 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
+ const int nMaxVal = o3tl::convert(128, o3tl::Length::pt, SmO3tlLengthUnit());
+ 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::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);
+
+ // Set the top and bottom of the root symbol to the top and bottom of its glyph bounding rect,
+ // to get accurate position of the root symbol.
+ SmRect rRootSymRect = pRootSym->AsGlyphRect();
+ pRootSym->SetTop(rRootSymRect.GetTop());
+ pRootSym->SetBottom(rRootSymRect.GetBottom());
+
+ 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 nMul;
+ if (o3tl::checked_multiply<tools::Long>(rOpRect.GetWidth(), rFormat.GetDistance(DIS_HORIZONTAL), nMul))
+ {
+ SAL_WARN("starmath", "integer overflow");
+ return;
+ }
+
+ tools::Long nDist = nMul / 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 (maText.getLength() == 1 && GetToken().aText[0] == ':')
+ Attributes() &= ~FontAttribute::Italic;
+
+ // Arabic text should not be italic, so we check for any character in Arabic script and
+ // remove italic attribute.
+ if (!maText.isEmpty())
+ {
+ sal_Int32 nIndex = 0;
+ while (nIndex < maText.getLength())
+ {
+ sal_uInt32 cChar = maText.iterateCodePoints(&nIndex);
+ if (u_getIntPropertyValue(cChar, UCHAR_SCRIPT) == USCRIPT_ARABIC)
+ {
+ Attributes() &= ~FontAttribute::Italic;
+ break;
+ }
+ }
+ }
+};
+
+
+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()));
+}
+
+/**************************************************************************/
+
+SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
+ : SmTextNode(eNodeType, rNodeToken, _nFontDesc)
+{
+}
+
+
+SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
+ : SmTextNode(SmNodeType::Special, rNodeToken, FNT_VARIABLE) // default Font isn't always correct!
+{
+}
+
+
+void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ const SmSym *pSym;
+ SmModule *pp = SM_MOD();
+
+ bool bIsGreekSymbol = false;
+ bool bIsSpecialSymbol = false;
+ bool bIsArabic = false;
+
+ if (nullptr != (pSym = pp->GetSymbolManager().GetSymbolByName(GetToken().aText.subView(1))))
+ {
+ sal_UCS4 cChar = pSym->GetCharacter();
+ OUString aTmp( &cChar, 1 );
+ SetText( aTmp );
+ GetFont() = pSym->GetFace(&rFormat);
+
+ OUString aSymbolSetName = SmLocalizedSymbolData::GetExportSymbolSetName(pSym->GetSymbolSetName());
+ if (aSymbolSetName == "Greek")
+ bIsGreekSymbol = true;
+ else if (aSymbolSetName == "Special")
+ bIsSpecialSymbol = true;
+ else if (aSymbolSetName == "Arabic")
+ bIsArabic = true;
+ }
+ 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;
+
+ sal_uInt32 cChar = 0;
+ if (!GetText().isEmpty())
+ {
+ sal_Int32 nIndex = 0;
+ cChar = GetText().iterateCodePoints(&nIndex);
+ if (!bIsArabic)
+ bIsArabic = u_getIntPropertyValue(cChar, UCHAR_SCRIPT) == USCRIPT_ARABIC;
+ }
+
+ if (!bIsGreekSymbol && !bIsSpecialSymbol && !bIsArabic)
+ return;
+
+ // Arabic and special symbols should not be italic,
+ // Greek is italic only in some cases.
+ bool bItalic = false;
+ if (bIsGreekSymbol)
+ {
+ sal_Int16 nStyle = rFormat.GetGreekCharStyle();
+ OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
+ if (nStyle == 1)
+ bItalic = true;
+ else if (nStyle == 2)
+ {
+ static const sal_Unicode cUppercaseAlpha = 0x0391;
+ static const sal_Unicode cUppercaseOmega = 0x03A9;
+ // 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 0000000000..2956dd9a81
--- /dev/null
+++ b/starmath/source/ooxmlexport.cxx
@@ -0,0 +1,596 @@
+/* -*- 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/imexport.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 != FormulaImExportBase::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 == FormulaImExportBase::eFormulaAlign::CENTER)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "center");
+ if (nAlign == FormulaImExportBase::eFormulaAlign::LEFT)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "left");
+ if (nAlign == FormulaImExportBase::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_376_1ST_EDITION == 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);
+ 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 0000000000..cd2e9d4465
--- /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 0000000000..4023a5e652
--- /dev/null
+++ b/starmath/source/ooxmlimport.cxx
@@ -0,0 +1,685 @@
+/* -*- 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( 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(" "
+ + readOMathArgInElement( M_TOKEN( e ))
+ + " ");
+ } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( e ))));
+ m_rStream.ensureClosingTag( M_TOKEN( eqArr ));
+ return "stack {" + ret + "}";
+}
+
+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 + "}";
+}
+
+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 0000000000..4f8df14bfd
--- /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 0000000000..66daec9d24
--- /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 0000000000..8f2815c3c3
--- /dev/null
+++ b/starmath/source/parse5.cxx
@@ -0,0 +1,2823 @@
+/* -*- 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 <symbol.hxx>
+#include <cfgitem.hxx>
+#include <starmathdatabase.hxx>
+
+#include <stack>
+
+using namespace ::com::sun::star::i18n;
+
+//Definition of math keywords
+const SmTokenTableEntry aTokenTable[]
+ = { { u"abs"_ustr, TABS, '\0', TG::UnOper, 13 },
+ { u"acute"_ustr, TACUTE, MS_ACUTE, TG::Attribute, 5 },
+ { u"aleph"_ustr, TALEPH, MS_ALEPH, TG::Standalone, 5 },
+ { u"alignb"_ustr, TALIGNC, '\0', TG::Align, 0 },
+ { u"alignc"_ustr, TALIGNC, '\0', TG::Align, 0 },
+ { u"alignl"_ustr, TALIGNL, '\0', TG::Align, 0 },
+ { u"alignm"_ustr, TALIGNC, '\0', TG::Align, 0 },
+ { u"alignr"_ustr, TALIGNR, '\0', TG::Align, 0 },
+ { u"alignt"_ustr, TALIGNC, '\0', TG::Align, 0 },
+ { u"and"_ustr, TAND, MS_AND, TG::Product, 0 },
+ { u"approx"_ustr, TAPPROX, MS_APPROX, TG::Relation, 0 },
+ { u"arccos"_ustr, TACOS, '\0', TG::Function, 5 },
+ { u"arccot"_ustr, TACOT, '\0', TG::Function, 5 },
+ { u"arcosh"_ustr, TACOSH, '\0', TG::Function, 5 },
+ { u"arcoth"_ustr, TACOTH, '\0', TG::Function, 5 },
+ { u"arcsin"_ustr, TASIN, '\0', TG::Function, 5 },
+ { u"arctan"_ustr, TATAN, '\0', TG::Function, 5 },
+ { u"arsinh"_ustr, TASINH, '\0', TG::Function, 5 },
+ { u"artanh"_ustr, TATANH, '\0', TG::Function, 5 },
+ { u"backepsilon"_ustr, TBACKEPSILON, MS_BACKEPSILON, TG::Standalone, 5 },
+ { u"bar"_ustr, TBAR, MS_BAR, TG::Attribute, 5 },
+ { u"binom"_ustr, TBINOM, '\0', TG::NONE, 5 },
+ { u"bold"_ustr, TBOLD, '\0', TG::FontAttr, 5 },
+ { u"boper"_ustr, TBOPER, '\0', TG::Product, 0 },
+ { u"breve"_ustr, TBREVE, MS_BREVE, TG::Attribute, 5 },
+ { u"bslash"_ustr, TBACKSLASH, MS_BACKSLASH, TG::Product, 0 },
+ { u"cdot"_ustr, TCDOT, MS_CDOT, TG::Product, 0 },
+ { u"check"_ustr, TCHECK, MS_CHECK, TG::Attribute, 5 },
+ { u"circ"_ustr, TCIRC, MS_CIRC, TG::Standalone, 5 },
+ { u"circle"_ustr, TCIRCLE, MS_CIRCLE, TG::Attribute, 5 },
+ { u"color"_ustr, TCOLOR, '\0', TG::FontAttr, 5 },
+ { u"coprod"_ustr, TCOPROD, MS_COPROD, TG::Oper, 5 },
+ { u"cos"_ustr, TCOS, '\0', TG::Function, 5 },
+ { u"cosh"_ustr, TCOSH, '\0', TG::Function, 5 },
+ { u"cot"_ustr, TCOT, '\0', TG::Function, 5 },
+ { u"coth"_ustr, TCOTH, '\0', TG::Function, 5 },
+ { u"csub"_ustr, TCSUB, '\0', TG::Power, 0 },
+ { u"csup"_ustr, TCSUP, '\0', TG::Power, 0 },
+ { u"dddot"_ustr, TDDDOT, MS_DDDOT, TG::Attribute, 5 },
+ { u"ddot"_ustr, TDDOT, MS_DDOT, TG::Attribute, 5 },
+ { u"def"_ustr, TDEF, MS_DEF, TG::Relation, 0 },
+ { u"div"_ustr, TDIV, MS_DIV, TG::Product, 0 },
+ { u"divides"_ustr, TDIVIDES, MS_LINE, TG::Relation, 0 },
+ { u"dlarrow"_ustr, TDLARROW, MS_DLARROW, TG::Standalone, 5 },
+ { u"dlrarrow"_ustr, TDLRARROW, MS_DLRARROW, TG::Standalone, 5 },
+ { u"dot"_ustr, TDOT, MS_DOT, TG::Attribute, 5 },
+ { u"dotsaxis"_ustr, TDOTSAXIS, MS_DOTSAXIS, TG::Standalone, 5 }, // 5 to continue expression
+ { u"dotsdiag"_ustr, TDOTSDIAG, MS_DOTSUP, TG::Standalone, 5 },
+ { u"dotsdown"_ustr, TDOTSDOWN, MS_DOTSDOWN, TG::Standalone, 5 },
+ { u"dotslow"_ustr, TDOTSLOW, MS_DOTSLOW, TG::Standalone, 5 },
+ { u"dotsup"_ustr, TDOTSUP, MS_DOTSUP, TG::Standalone, 5 },
+ { u"dotsvert"_ustr, TDOTSVERT, MS_DOTSVERT, TG::Standalone, 5 },
+ { u"downarrow"_ustr, TDOWNARROW, MS_DOWNARROW, TG::Standalone, 5 },
+ { u"drarrow"_ustr, TDRARROW, MS_DRARROW, TG::Standalone, 5 },
+ { u"emptyset"_ustr, TEMPTYSET, MS_EMPTYSET, TG::Standalone, 5 },
+ { u"equiv"_ustr, TEQUIV, MS_EQUIV, TG::Relation, 0 },
+ { u"evaluate"_ustr, TEVALUATE, '\0', TG::NONE, 0 },
+ { u"exists"_ustr, TEXISTS, MS_EXISTS, TG::Standalone, 5 },
+ { u"exp"_ustr, TEXP, '\0', TG::Function, 5 },
+ { u"fact"_ustr, TFACT, MS_FACT, TG::UnOper, 5 },
+ { u"fixed"_ustr, TFIXED, '\0', TG::Font, 0 },
+ { u"font"_ustr, TFONT, '\0', TG::FontAttr, 5 },
+ { u"forall"_ustr, TFORALL, MS_FORALL, TG::Standalone, 5 },
+ { u"fourier"_ustr, TFOURIER, MS_FOURIER, TG::Standalone, 5 },
+ { u"frac"_ustr, TFRAC, '\0', TG::NONE, 5 },
+ { u"from"_ustr, TFROM, '\0', TG::Limit, 0 },
+ { u"func"_ustr, TFUNC, '\0', TG::Function, 5 },
+ { u"ge"_ustr, TGE, MS_GE, TG::Relation, 0 },
+ { u"geslant"_ustr, TGESLANT, MS_GESLANT, TG::Relation, 0 },
+ { u"gg"_ustr, TGG, MS_GG, TG::Relation, 0 },
+ { u"grave"_ustr, TGRAVE, MS_GRAVE, TG::Attribute, 5 },
+ { u"gt"_ustr, TGT, MS_GT, TG::Relation, 0 },
+ { u"hadd"_ustr, THADD, MS_HADD, TG::Oper, 5 },
+ { u"harpoon"_ustr, THARPOON, MS_HARPOON, TG::Attribute, 5 },
+ { u"hat"_ustr, THAT, MS_HAT, TG::Attribute, 5 },
+ { u"hbar"_ustr, THBAR, MS_HBAR, TG::Standalone, 5 },
+ { u"hex"_ustr, THEX, '\0', TG::NONE, 5 },
+ { u"iiint"_ustr, TIIINT, MS_IIINT, TG::Oper, 5 },
+ { u"iint"_ustr, TIINT, MS_IINT, TG::Oper, 5 },
+ { u"im"_ustr, TIM, MS_IM, TG::Standalone, 5 },
+ { u"in"_ustr, TIN, MS_IN, TG::Relation, 0 },
+ { u"infinity"_ustr, TINFINITY, MS_INFINITY, TG::Standalone, 5 },
+ { u"infty"_ustr, TINFINITY, MS_INFINITY, TG::Standalone, 5 },
+ { u"int"_ustr, TINT, MS_INT, TG::Oper, 5 },
+ { u"intd"_ustr, TINTD, MS_INT, TG::Oper, 5 },
+ { u"intersection"_ustr, TINTERSECT, MS_INTERSECT, TG::Product, 0 },
+ { u"it"_ustr, TIT, '\0', TG::Product, 0 },
+ { u"ital"_ustr, TITALIC, '\0', TG::FontAttr, 5 },
+ { u"italic"_ustr, TITALIC, '\0', TG::FontAttr, 5 },
+ { u"lambdabar"_ustr, TLAMBDABAR, MS_LAMBDABAR, TG::Standalone, 5 },
+ { u"langle"_ustr, TLANGLE, MS_LMATHANGLE, TG::LBrace, 5 },
+ { u"laplace"_ustr, TLAPLACE, MS_LAPLACE, TG::Standalone, 5 },
+ { u"lbrace"_ustr, TLBRACE, MS_LBRACE, TG::LBrace, 5 },
+ { u"lceil"_ustr, TLCEIL, MS_LCEIL, TG::LBrace, 5 },
+ { u"ldbracket"_ustr, TLDBRACKET, MS_LDBRACKET, TG::LBrace, 5 },
+ { u"ldline"_ustr, TLDLINE, MS_DVERTLINE, TG::LBrace, 5 },
+ { u"le"_ustr, TLE, MS_LE, TG::Relation, 0 },
+ { u"left"_ustr, TLEFT, '\0', TG::NONE, 5 },
+ { u"leftarrow"_ustr, TLEFTARROW, MS_LEFTARROW, TG::Standalone, 5 },
+ { u"leslant"_ustr, TLESLANT, MS_LESLANT, TG::Relation, 0 },
+ { u"lfloor"_ustr, TLFLOOR, MS_LFLOOR, TG::LBrace, 5 },
+ { u"lim"_ustr, TLIM, '\0', TG::Oper, 5 },
+ { u"liminf"_ustr, TLIMINF, '\0', TG::Oper, 5 },
+ { u"limsup"_ustr, TLIMSUP, '\0', TG::Oper, 5 },
+ { u"lint"_ustr, TLINT, MS_LINT, TG::Oper, 5 },
+ { u"ll"_ustr, TLL, MS_LL, TG::Relation, 0 },
+ { u"lline"_ustr, TLLINE, MS_VERTLINE, TG::LBrace, 5 },
+ { u"llint"_ustr, TLLINT, MS_LLINT, TG::Oper, 5 },
+ { u"lllint"_ustr, TLLLINT, MS_LLLINT, TG::Oper, 5 },
+ { u"ln"_ustr, TLN, '\0', TG::Function, 5 },
+ { u"log"_ustr, TLOG, '\0', TG::Function, 5 },
+ { u"lrline"_ustr, TLRLINE, MS_VERTLINE, TG::LBrace | TG::RBrace, 5 },
+ { u"lrdline"_ustr, TLRDLINE, MS_VERTLINE, TG::LBrace | TG::RBrace, 5 },
+ { u"lsub"_ustr, TLSUB, '\0', TG::Power, 0 },
+ { u"lsup"_ustr, TLSUP, '\0', TG::Power, 0 },
+ { u"lt"_ustr, TLT, MS_LT, TG::Relation, 0 },
+ { u"maj"_ustr, TSUM, MS_MAJ, TG::Oper, 5 },
+ { u"matrix"_ustr, TMATRIX, '\0', TG::NONE, 5 },
+ { u"minusplus"_ustr, TMINUSPLUS, MS_MINUSPLUS, TG::UnOper | TG::Sum, 5 },
+ { u"mline"_ustr, TMLINE, MS_VERTLINE, TG::NONE, 0 }, //! not in TG::RBrace, Level 0
+ { u"nabla"_ustr, TNABLA, MS_NABLA, TG::Standalone, 5 },
+ { u"nbold"_ustr, TNBOLD, '\0', TG::FontAttr, 5 },
+ { u"ndivides"_ustr, TNDIVIDES, MS_NDIVIDES, TG::Relation, 0 },
+ { u"neg"_ustr, TNEG, MS_NEG, TG::UnOper, 5 },
+ { u"neq"_ustr, TNEQ, MS_NEQ, TG::Relation, 0 },
+ { u"newline"_ustr, TNEWLINE, '\0', TG::NONE, 0 },
+ { u"ni"_ustr, TNI, MS_NI, TG::Relation, 0 },
+ { u"nitalic"_ustr, TNITALIC, '\0', TG::FontAttr, 5 },
+ { u"none"_ustr, TNONE, '\0', TG::LBrace | TG::RBrace, 0 },
+ { u"nospace"_ustr, TNOSPACE, '\0', TG::Standalone, 5 },
+ { u"notexists"_ustr, TNOTEXISTS, MS_NOTEXISTS, TG::Standalone, 5 },
+ { u"notin"_ustr, TNOTIN, MS_NOTIN, TG::Relation, 0 },
+ { u"nprec"_ustr, TNOTPRECEDES, MS_NOTPRECEDES, TG::Relation, 0 },
+ { u"nroot"_ustr, TNROOT, MS_SQRT, TG::UnOper, 5 },
+ { u"nsubset"_ustr, TNSUBSET, MS_NSUBSET, TG::Relation, 0 },
+ { u"nsubseteq"_ustr, TNSUBSETEQ, MS_NSUBSETEQ, TG::Relation, 0 },
+ { u"nsucc"_ustr, TNOTSUCCEEDS, MS_NOTSUCCEEDS, TG::Relation, 0 },
+ { u"nsupset"_ustr, TNSUPSET, MS_NSUPSET, TG::Relation, 0 },
+ { u"nsupseteq"_ustr, TNSUPSETEQ, MS_NSUPSETEQ, TG::Relation, 0 },
+ { u"odivide"_ustr, TODIVIDE, MS_ODIVIDE, TG::Product, 0 },
+ { u"odot"_ustr, TODOT, MS_ODOT, TG::Product, 0 },
+ { u"ominus"_ustr, TOMINUS, MS_OMINUS, TG::Sum, 0 },
+ { u"oper"_ustr, TOPER, '\0', TG::Oper, 5 },
+ { u"oplus"_ustr, TOPLUS, MS_OPLUS, TG::Sum, 0 },
+ { u"or"_ustr, TOR, MS_OR, TG::Sum, 0 },
+ { u"ortho"_ustr, TORTHO, MS_ORTHO, TG::Relation, 0 },
+ { u"otimes"_ustr, TOTIMES, MS_OTIMES, TG::Product, 0 },
+ { u"over"_ustr, TOVER, '\0', TG::Product, 0 },
+ { u"overbrace"_ustr, TOVERBRACE, MS_OVERBRACE, TG::Product, 5 },
+ { u"overline"_ustr, TOVERLINE, '\0', TG::Attribute, 5 },
+ { u"overstrike"_ustr, TOVERSTRIKE, '\0', TG::Attribute, 5 },
+ { u"owns"_ustr, TNI, MS_NI, TG::Relation, 0 },
+ { u"parallel"_ustr, TPARALLEL, MS_DLINE, TG::Relation, 0 },
+ { u"partial"_ustr, TPARTIAL, MS_PARTIAL, TG::Standalone, 5 },
+ { u"phantom"_ustr, TPHANTOM, '\0', TG::FontAttr, 5 },
+ { u"plusminus"_ustr, TPLUSMINUS, MS_PLUSMINUS, TG::UnOper | TG::Sum, 5 },
+ { u"prec"_ustr, TPRECEDES, MS_PRECEDES, TG::Relation, 0 },
+ { u"preccurlyeq"_ustr, TPRECEDESEQUAL, MS_PRECEDESEQUAL, TG::Relation, 0 },
+ { u"precsim"_ustr, TPRECEDESEQUIV, MS_PRECEDESEQUIV, TG::Relation, 0 },
+ { u"prod"_ustr, TPROD, MS_PROD, TG::Oper, 5 },
+ { u"prop"_ustr, TPROP, MS_PROP, TG::Relation, 0 },
+ { u"rangle"_ustr, TRANGLE, MS_RMATHANGLE, TG::RBrace, 0 }, //! 0 to terminate expression
+ { u"rbrace"_ustr, TRBRACE, MS_RBRACE, TG::RBrace, 0 },
+ { u"rceil"_ustr, TRCEIL, MS_RCEIL, TG::RBrace, 0 },
+ { u"rdbracket"_ustr, TRDBRACKET, MS_RDBRACKET, TG::RBrace, 0 },
+ { u"rdline"_ustr, TRDLINE, MS_DVERTLINE, TG::RBrace, 0 },
+ { u"re"_ustr, TRE, MS_RE, TG::Standalone, 5 },
+ { u"rfloor"_ustr, TRFLOOR, MS_RFLOOR, TG::RBrace, 0 }, //! 0 to terminate expression
+ { u"right"_ustr, TRIGHT, '\0', TG::NONE, 0 },
+ { u"rightarrow"_ustr, TRIGHTARROW, MS_RIGHTARROW, TG::Standalone, 5 },
+ { u"rline"_ustr, TRLINE, MS_VERTLINE, TG::RBrace, 0 }, //! 0 to terminate expression
+ { u"rsub"_ustr, TRSUB, '\0', TG::Power, 0 },
+ { u"rsup"_ustr, TRSUP, '\0', TG::Power, 0 },
+ { u"sans"_ustr, TSANS, '\0', TG::Font, 0 },
+ { u"serif"_ustr, TSERIF, '\0', TG::Font, 0 },
+ { u"setC"_ustr, TSETC, MS_SETC, TG::Standalone, 5 },
+ { u"setminus"_ustr, TSETMINUS, MS_BACKSLASH, TG::Product, 0 },
+ { u"setN"_ustr, TSETN, MS_SETN, TG::Standalone, 5 },
+ { u"setQ"_ustr, TSETQ, MS_SETQ, TG::Standalone, 5 },
+ { u"setquotient"_ustr, TSETQUOTIENT, MS_SLASH, TG::Product, 0 },
+ { u"setR"_ustr, TSETR, MS_SETR, TG::Standalone, 5 },
+ { u"setZ"_ustr, TSETZ, MS_SETZ, TG::Standalone, 5 },
+ { u"sim"_ustr, TSIM, MS_SIM, TG::Relation, 0 },
+ { u"simeq"_ustr, TSIMEQ, MS_SIMEQ, TG::Relation, 0 },
+ { u"sin"_ustr, TSIN, '\0', TG::Function, 5 },
+ { u"sinh"_ustr, TSINH, '\0', TG::Function, 5 },
+ { u"size"_ustr, TSIZE, '\0', TG::FontAttr, 5 },
+ { u"slash"_ustr, TSLASH, MS_SLASH, TG::Product, 0 },
+ { u"sqrt"_ustr, TSQRT, MS_SQRT, TG::UnOper, 5 },
+ { u"stack"_ustr, TSTACK, '\0', TG::NONE, 5 },
+ { u"sub"_ustr, TRSUB, '\0', TG::Power, 0 },
+ { u"subset"_ustr, TSUBSET, MS_SUBSET, TG::Relation, 0 },
+ { u"subseteq"_ustr, TSUBSETEQ, MS_SUBSETEQ, TG::Relation, 0 },
+ { u"succ"_ustr, TSUCCEEDS, MS_SUCCEEDS, TG::Relation, 0 },
+ { u"succcurlyeq"_ustr, TSUCCEEDSEQUAL, MS_SUCCEEDSEQUAL, TG::Relation, 0 },
+ { u"succsim"_ustr, TSUCCEEDSEQUIV, MS_SUCCEEDSEQUIV, TG::Relation, 0 },
+ { u"sum"_ustr, TSUM, MS_SUM, TG::Oper, 5 },
+ { u"sup"_ustr, TRSUP, '\0', TG::Power, 0 },
+ { u"supset"_ustr, TSUPSET, MS_SUPSET, TG::Relation, 0 },
+ { u"supseteq"_ustr, TSUPSETEQ, MS_SUPSETEQ, TG::Relation, 0 },
+ { u"tan"_ustr, TTAN, '\0', TG::Function, 5 },
+ { u"tanh"_ustr, TTANH, '\0', TG::Function, 5 },
+ { u"tilde"_ustr, TTILDE, MS_TILDE, TG::Attribute, 5 },
+ { u"times"_ustr, TTIMES, MS_TIMES, TG::Product, 0 },
+ { u"to"_ustr, TTO, '\0', TG::Limit, 0 },
+ { u"toward"_ustr, TTOWARD, MS_RIGHTARROW, TG::Relation, 0 },
+ { u"transl"_ustr, TTRANSL, MS_TRANSL, TG::Relation, 0 },
+ { u"transr"_ustr, TTRANSR, MS_TRANSR, TG::Relation, 0 },
+ { u"underbrace"_ustr, TUNDERBRACE, MS_UNDERBRACE, TG::Product, 5 },
+ { u"underline"_ustr, TUNDERLINE, '\0', TG::Attribute, 5 },
+ { u"union"_ustr, TUNION, MS_UNION, TG::Sum, 0 },
+ { u"uoper"_ustr, TUOPER, '\0', TG::UnOper, 5 },
+ { u"uparrow"_ustr, TUPARROW, MS_UPARROW, TG::Standalone, 5 },
+ { u"vec"_ustr, TVEC, MS_VEC, TG::Attribute, 5 },
+ { u"widebslash"_ustr, TWIDEBACKSLASH, MS_BACKSLASH, TG::Product, 0 },
+ { u"wideharpoon"_ustr, TWIDEHARPOON, MS_HARPOON, TG::Attribute, 5 },
+ { u"widehat"_ustr, TWIDEHAT, MS_HAT, TG::Attribute, 5 },
+ { u"wideslash"_ustr, TWIDESLASH, MS_SLASH, TG::Product, 0 },
+ { u"widetilde"_ustr, TWIDETILDE, MS_TILDE, TG::Attribute, 5 },
+ { u"widevec"_ustr, TWIDEVEC, MS_VEC, TG::Attribute, 5 },
+ { u"wp"_ustr, TWP, MS_WP, TG::Standalone, 5 },
+ { u"جا"_ustr, TSIN, '\0', TG::Function, 5 },
+ { u"جاز"_ustr, TSINH, '\0', TG::Function, 5 },
+ { u"جتا"_ustr, TCOS, '\0', TG::Function, 5 },
+ { u"جتاز"_ustr, TCOSH, '\0', TG::Function, 5 },
+ { u"حا"_ustr, TSIN, '\0', TG::Function, 5 },
+ { u"حاز"_ustr, TSINH, '\0', TG::Function, 5 },
+ { u"حتا"_ustr, TCOS, '\0', TG::Function, 5 },
+ { u"حتاز"_ustr, TCOSH, '\0', TG::Function, 5 },
+ { u"حد"_ustr, THADD, MS_HADD, TG::Oper, 5 },
+ { u"طا"_ustr, TTAN, '\0', TG::Function, 5 },
+ { u"طاز"_ustr, TTANH, '\0', TG::Function, 5 },
+ { u"طتا"_ustr, TCOT, '\0', TG::Function, 5 },
+ { u"طتاز"_ustr, TCOTH, '\0', TG::Function, 5 },
+ { u"ظا"_ustr, TTAN, '\0', TG::Function, 5 },
+ { u"ظاز"_ustr, TTANH, '\0', TG::Function, 5 },
+ { u"ظتا"_ustr, TCOT, '\0', TG::Function, 5 },
+ { u"ظتاز"_ustr, TCOTH, '\0', TG::Function, 5 },
+ { u"قا"_ustr, TSEC, '\0', TG::Function, 5 },
+ { u"قاز"_ustr, TSECH, '\0', TG::Function, 5 },
+ { u"قتا"_ustr, TCSC, '\0', TG::Function, 5 },
+ { u"قتاز"_ustr, TCSCH, '\0', TG::Function, 5 },
+ { u"لو"_ustr, TLOG, '\0', TG::Function, 5 },
+ { u"مجـ"_ustr, TSUM, MS_MAJ, TG::Oper, 5 },
+ { u"نها"_ustr, TNAHA, '\0', TG::Oper, 5 },
+ { u"ٯا"_ustr, TSEC, '\0', TG::Function, 5 },
+ { u"ٯاز"_ustr, TSECH, '\0', TG::Function, 5 },
+ { u"ٯتا"_ustr, TCSC, '\0', TG::Function, 5 },
+ { u"ٯتاز"_ustr, TCSCH, '\0', TG::Function, 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, std::u16string_view aText)
+{
+ assert(nPos + nLen <= m_aBufferString.getLength()); //checks if length allows text replace
+
+ m_aBufferString = m_aBufferString.replaceAt(nPos, nLen, aText); //replace and reindex
+ sal_Int32 nChg = aText.size() - 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ m_aCurToken.nGroup = TG::Power;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "^";
+ }
+ break;
+ case '`':
+ {
+ m_aCurToken.eType = TSBLANK;
+ m_aCurToken.cMathChar = u""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "##";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else
+ {
+ m_aCurToken.eType = TPOUND;
+ m_aCurToken.cMathChar = u""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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""_ustr;
+ 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:
+ case THADD:
+ case TNAHA:
+ if (eType == TLIMSUP)
+ m_aCurToken.aText = u"lim sup"_ustr;
+ else if (eType == TLIMINF)
+ m_aCurToken.aText = u"lim inf"_ustr;
+ else if (eType == TNAHA)
+ m_aCurToken.aText = u"نها"_ustr;
+ else if (eType == THADD)
+ m_aCurToken.aText = OUString(&MS_HADD, 1);
+ else
+ m_aCurToken.aText = u"lim"_ustr;
+ 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())
+ {
+ const SmSym* pSym
+ = SM_MOD()->GetSymbolManager().GetSymbolByExportName(rName.subView(1));
+ if (pSym)
+ {
+ aNewName = pSym->GetUiName();
+ bReplace = true;
+ }
+ }
+ else if (IsExportSymbolNames())
+ {
+ const SmSym* pSym = SM_MOD()->GetSymbolManager().GetSymbolByUiName(rName.subView(1));
+ if (pSym)
+ {
+ aNewName = pSym->GetExportName();
+ 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 0000000000..31c1e40c9c
--- /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 0000000000..807ab7d0c0
--- /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(std::u16string_view aText)
+ // 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 (aText.empty())
+ return false;
+
+ OSL_ENSURE(aText.size() == 1, "Sm : string must be exactly one character long");
+ sal_Unicode cChar = aText[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 0000000000..ee5c486eae
--- /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 0000000000..e8c38aaa6c
--- /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 0000000000..80231c21b7
--- /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 <comphelper/diagnose_ex.hxx>
+
+#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 0000000000..81a68d9099
--- /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 0000000000..8c9e88bda4
--- /dev/null
+++ b/starmath/source/smdll.cxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * 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 <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(true, pModule);
+
+ SmCmdBoxWrapper::RegisterChildWindow(true);
+ }
+}
+
+namespace SmGlobals
+{
+ void ensure()
+ {
+ static SmDLL theDll;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/smediteng.cxx b/starmath/source/smediteng.cxx
new file mode 100644
index 0000000000..9eac0863e3
--- /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""_ustr);
+
+ // 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 0000000000..0ffb6035c1
--- /dev/null
+++ b/starmath/source/smmod.cxx
@@ -0,0 +1,238 @@
+/* -*- 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].getId()))
+ {
+ 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].getId();
+ 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].getId()))
+ {
+ 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].getId();
+ 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"_ostr, {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( !moSysLocale )
+ moSysLocale.emplace();
+ return *moSysLocale;
+}
+
+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,
+ SID_INLINE_EDIT_ENABLE, SID_INLINE_EDIT_ENABLE>);
+
+ 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 0000000000..4bb854f123
--- /dev/null
+++ b/starmath/source/symbol.cxx
@@ -0,0 +1,309 @@
+/* -*- 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 <format.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+
+SmSym::SmSym() :
+ m_aUiName(OUString("unknown")),
+ m_aSetName(OUString("unknown")),
+ m_cChar('\0'),
+ m_bPredefined(false)
+{
+ m_aExportName = m_aUiName;
+ 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_aUiName = 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_aUiName = rSymbol.m_aUiName;
+ 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_aUiName == rSymbol.m_aUiName &&
+ m_aFace == rSymbol.m_aFace &&
+ m_cChar == rSymbol.m_cChar;
+}
+
+const vcl::Font& SmSym::GetFace(const SmFormat* pFormat) const
+{
+ if (m_aFace.GetFamilyName().isEmpty())
+ {
+ if (!pFormat)
+ pFormat = &SM_MOD()->GetConfig()->GetStandardFormat();
+ return pFormat->GetFont(FNT_VARIABLE);
+ }
+ return m_aFace;
+}
+
+/**************************************************************************/
+
+
+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(std::u16string_view rSymbolName)
+{
+ SmSym* pRes = GetSymbolByUiName(rSymbolName);
+ if (!pRes)
+ pRes = GetSymbolByExportName(rSymbolName);
+ return pRes;
+}
+
+SmSym *SmSymbolManager::GetSymbolByUiName(std::u16string_view rSymbolName)
+{
+ OUString aSymbolName(rSymbolName);
+ SmSym *pRes = nullptr;
+ SymbolMap_t::iterator aIt( m_aSymbols.find( aSymbolName ) );
+ if (aIt != m_aSymbols.end())
+ pRes = &aIt->second;
+ return pRes;
+}
+
+SmSym* SmSymbolManager::GetSymbolByExportName(std::u16string_view rSymbolName)
+{
+ SmSym* pRes = nullptr;
+ for (auto& rPair : m_aSymbols)
+ {
+ SmSym& rSymbol = rPair.second;
+ if (rSymbol.GetExportName() == rSymbolName)
+ {
+ pRes = &rSymbol;
+ break;
+ }
+ }
+ 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.GetUiName() );
+ if (!aSymbolName.isEmpty() && !rSymbol.GetSymbolSetName().isEmpty())
+ {
+ const SmSym *pFound = GetSymbolByUiName( 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.GetUiName().isEmpty(), "symbol without name!" );
+ if (!rSym.GetUiName().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.GetUiName();
+ SmSym aSymbol( aSymbolName, aFont, rSym.GetCharacter(),
+ aSymbolSetName, true /*bIsPredefined*/ );
+ aSymbol.SetExportName("i" + rSym.GetExportName());
+
+ 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 0000000000..077977b39d
--- /dev/null
+++ b/starmath/source/tmpdevice.cxx
@@ -0,0 +1,68 @@
+/* -*- 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 <utility.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 && SmMapUnit() != rOutDev.GetMapMode().GetMapUnit())
+ {
+ SAL_WARN("starmath", "incorrect MapMode?");
+ rOutDev.SetMapMode(MapMode(SmMapUnit())); // 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 0000000000..9c15fe0357
--- /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 0000000000..ba7910747b
--- /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 0000000000..3c125077c4
--- /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 0000000000..0b73175b00
--- /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 <comphelper/diagnose_ex.hxx>
+
+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 0000000000..ea12928d3d
--- /dev/null
+++ b/starmath/source/unomodel.cxx
@@ -0,0 +1,1109 @@
+/* -*- 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 <editeng/paperinf.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 ::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_MATH,
+ 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_MATH_POSTURE,
+ HANDLE_FONT_MATH_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_IS_RIGHT_TO_LEFT,
+ 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("FontMathIsBold") , HANDLE_FONT_MATH_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_MATH },
+ { OUString("FontMathIsItalic") , HANDLE_FONT_MATH_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_MATH },
+ { OUString("FontNameFunctions") , HANDLE_FONT_NAME_FUNCTIONS , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_FUNCTION },
+ { OUString("FontNameMath") , HANDLE_FONT_NAME_MATH , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_MATH },
+ { 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("IsRightToLeft") , HANDLE_IS_RIGHT_TO_LEFT , 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,
+ // 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 OUString service1 = u"com.sun.star.document.OfficeDocument"_ustr;
+ static constexpr OUString service2 = u"com.sun.star.formula.FormulaProperties"_ustr;
+ 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_MATH :
+ 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();
+ maFonts[(*ppEntries)->mnMemberId].SetFamilyName(sFontName);
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_POSTURE:
+ case HANDLE_CUSTOM_FONT_SANS_POSTURE :
+ case HANDLE_CUSTOM_FONT_SERIF_POSTURE:
+ case HANDLE_FONT_MATH_POSTURE :
+ case HANDLE_FONT_VARIABLES_POSTURE :
+ case HANDLE_FONT_FUNCTIONS_POSTURE :
+ case HANDLE_FONT_NUMBERS_POSTURE :
+ case HANDLE_FONT_TEXT_POSTURE :
+ {
+ std::optional<const bool> bVal = o3tl::tryAccess<bool>(*pValues);
+ if(!bVal.has_value())
+ throw IllegalArgumentException();
+ maFonts[(*ppEntries)->mnMemberId].SetItalic(*bVal ? ITALIC_NORMAL : ITALIC_NONE);
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SANS_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SERIF_WEIGHT :
+ case HANDLE_FONT_MATH_WEIGHT :
+ case HANDLE_FONT_VARIABLES_WEIGHT :
+ case HANDLE_FONT_FUNCTIONS_WEIGHT :
+ case HANDLE_FONT_NUMBERS_WEIGHT :
+ case HANDLE_FONT_TEXT_WEIGHT :
+ {
+ std::optional<const bool> bVal = o3tl::tryAccess<bool>(*pValues);
+ if(!bVal.has_value())
+ throw IllegalArgumentException();
+ maFonts[(*ppEntries)->mnMemberId].SetWeight(*bVal ? WEIGHT_BOLD : WEIGHT_NORMAL);
+ }
+ break;
+ case HANDLE_BASE_FONT_HEIGHT :
+ {
+ // Point!
+ sal_Int16 nVal = lcl_AnyToINT16(*pValues);
+ if(nVal < 1)
+ throw IllegalArgumentException();
+ Size aSize = aFormat.GetBaseSize();
+ aSize.setHeight(o3tl::convert(nVal, o3tl::Length::pt, SmO3tlLengthUnit()));
+ aFormat.SetBaseSize(aSize);
+
+ // apply base size to fonts
+ const Size aTmp( aFormat.GetBaseSize() );
+ for (sal_uInt16 i = FNT_BEGIN; i <= FNT_END; i++)
+ maFonts[i].SetSize(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_IS_RIGHT_TO_LEFT :
+ aFormat.SetRightToLeft(*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,
+ SID_INLINE_EDIT_ENABLE, SID_INLINE_EDIT_ENABLE>> ( 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_Int16>());
+ break;
+ }
+ }
+
+ // tdf#143213
+ // Collect all font settings and apply them at the end, since the font name change can be seen
+ // after italic or bold settings and would then override them.
+ for (sal_uInt16 nFontDesc = FNT_BEGIN; nFontDesc <= FNT_END; ++nFontDesc)
+ {
+ const SmFace& rFont = maFonts[nFontDesc];
+ if (rFont.GetFamilyName().isEmpty())
+ continue;
+
+ if (aFormat.GetFont(nFontDesc).GetFamilyName() != rFont.GetFamilyName())
+ {
+ const SmFace rOld = aFormat.GetFont(nFontDesc);
+
+ SmFace aSet(rFont.GetFamilyName(), rOld.GetFontSize());
+ aSet.SetBorderWidth(rOld.GetBorderWidth());
+ aSet.SetAlignment(ALIGN_BASELINE);
+ aFormat.SetFont(nFontDesc, aSet);
+ }
+
+ if (aFormat.GetFont(nFontDesc).GetItalic() != rFont.GetItalic())
+ {
+ vcl::Font aNewFont(aFormat.GetFont(nFontDesc));
+ aNewFont.SetItalic(rFont.GetItalic());
+ aFormat.SetFont(nFontDesc, aNewFont);
+ }
+
+ if (aFormat.GetFont(nFontDesc).GetWeight() != rFont.GetWeight())
+ {
+ vcl::Font aNewFont(aFormat.GetFont(nFontDesc));
+ aNewFont.SetWeight(rFont.GetWeight());
+ aFormat.SetFont(nFontDesc, aNewFont);
+ }
+
+ if (aFormat.GetFont(nFontDesc).GetFontSize() != rFont.GetFontSize())
+ {
+ aFormat.SetFontSize(nFontDesc, rFont.GetFontSize());
+ }
+ }
+
+ 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_MATH :
+ 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_MATH_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_MATH_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(o3tl::convert(aFormat.GetBaseSize().Height(),
+ SmO3tlLengthUnit(), o3tl::Length::pt));
+ }
+ 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_IS_RIGHT_TO_LEFT :
+ *pValue <<= aFormat.IsRightToLeft();
+ 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();
+ std::vector < const SmSym * > aVector;
+
+ const SymbolPtrVec_t aSymbols( rManager.GetSymbols() );
+ for (const SmSym* pSymbol : aSymbols)
+ {
+ if (pSymbol && !pSymbol->IsPredefined() &&
+ (!bUsedSymbolsOnly ||
+ rUsedSymbols.find( pSymbol->GetUiName() ) != rUsedSymbols.end()))
+ aVector.push_back ( pSymbol );
+ }
+ Sequence < SymbolDescriptor > aSequence ( aVector.size() );
+ SymbolDescriptor * pDescriptor = aSequence.getArray();
+
+ for (const SmSym* pSymbol : aVector)
+ {
+ pDescriptor->sName = pSymbol->GetUiName();
+ 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;
+}
+
+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 );
+ Size aPrtPaperSize;
+ if (Printer *pPrinter = aPrinterAccess.GetPrinter())
+ {
+ // tdf#157965: UNO methods are expected to return sizes in mm/100
+ pPrinter->SetMapMode(MapMode(MapUnit::Map100thMM)); // reset in SmPrinterAccess dtor
+ aPrtPaperSize = pPrinter->GetPaperSize();
+ }
+
+ // if paper size is 0 (usually if no 'real' printer is found),
+ // guess the paper size
+ if (aPrtPaperSize.IsEmpty())
+ aPrtPaperSize = SvxPaperInfo::GetDefaultPaperSize(MapUnit::Map100thMM);
+ 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 = dynamic_cast<VCLXDevice*>( xRenderDevice.get() );
+ VclPtr< OutputDevice> pOut = pDevice ? pDevice->GetOutputDevice()
+ : VclPtr< OutputDevice >();
+ if (!pOut)
+ throw RuntimeException();
+
+ pOut->SetMapMode(MapMode(SmMapUnit()));
+
+ uno::Reference< frame::XModel > xModel;
+ rSelection >>= xModel;
+ if (xModel != pDocSh->GetModel())
+ return;
+
+ SmPrinterAccess aPrinterAccess( *pDocSh );
+
+ Size aPrtPaperSize;
+ Size aOutputSize;
+ Point aPrtPageOffset;
+ if (Printer *pPrinter = aPrinterAccess.GetPrinter())
+ {
+ aPrtPaperSize = pPrinter->GetPaperSize();
+ aOutputSize = pPrinter->GetOutputSize();
+ aPrtPageOffset = pPrinter->GetPageOffset();
+ }
+
+ // no real printer ??
+ if (aPrtPaperSize.IsEmpty())
+ {
+ aPrtPaperSize = SvxPaperInfo::GetDefaultPaperSize(SmMapUnit());
+ // 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 );
+
+ pDocSh->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 o3tl::convert(static_cast< SmDocShell* >( GetObjectShell())->GetSize(), SmO3tlLengthUnit(), o3tl::Length::mm100);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/utility.cxx b/starmath/source/utility.cxx
new file mode 100644
index 0000000000..4a9225f6b3
--- /dev/null
+++ b/starmath/source/utility.cxx
@@ -0,0 +1,245 @@
+/* -*- 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(", " + SmResId(RID_FONTITALIC));
+ }
+ if (IsBold( rFont ))
+ {
+ 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
+ const int nMinVal = o3tl::convert(2, o3tl::Length::pt, SmO3tlLengthUnit());
+
+ 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 0000000000..8f88b5ced6
--- /dev/null
+++ b/starmath/source/view.cxx
@@ -0,0 +1,2239 @@
+/* -*- 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/frame/XModel.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 <LibreOfficeKit/LibreOfficeKitEnums.h>
+#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/lokcomponenthelpers.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxbasecontroller.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <sfx2/viewfac.hxx>
+#include <svl/eitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/voiditem.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 <editeng/editund2.hxx>
+#include <svx/svxdlg.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <sal/log.hxx>
+#include <tools/svborder.hxx>
+#include <o3tl/string_view.hxx>
+#include <o3tl/temporary.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>
+
+// 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 visibility 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->GetOutputDevice();
+ MapMode aMap( rNewMapMode );
+ aMap.SetOrigin( aMap.GetOrigin() + rDevice.PixelToLogic( aPixOffset, aMap ) );
+ rDevice.SetMapMode( aMap );
+ mxGraphic->Invalidate();
+}
+
+MapMode SmGraphicWindow::GetGraphicMapMode() const
+{
+ OutputDevice& rDevice = mxGraphic->GetOutputDevice();
+ MapMode aMap(rDevice.GetMapMode());
+ aMap.SetOrigin( aMap.GetOrigin() - rDevice.PixelToLogic( aPixOffset ) );
+ return aMap;
+}
+
+void SmGraphicWindow::SetTotalSize( const Size& rNewSize )
+{
+ aTotPixSz = mxGraphic->GetOutputDevice().LogicToPixel(rNewSize);
+ Resize();
+}
+
+Size SmGraphicWindow::GetTotalSize() const
+{
+ return mxGraphic->GetOutputDevice().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 = GetOutputDevice();
+
+ rDevice.EnableRTL(GetDoc()->GetFormat().IsRightToLeft());
+ rDevice.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Disable map mode, so that it's possible to send mouse event coordinates
+ // directly in twips.
+ rDevice.EnableMapMode(false);
+ }
+ else
+ {
+ const Fraction aFraction(1, 1);
+ rDevice.SetMapMode(MapMode(SmMapUnit(), 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();
+}
+
+SmDocShell* SmGraphicWidget::GetDoc() { return GetView().GetDoc(); }
+
+SmCursor& SmGraphicWidget::GetCursor()
+{
+ assert(GetDoc());
+ return GetDoc()->GetCursor();
+}
+
+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 = GetOutputDevice();
+ // get click position relative to formula
+ Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
+
+ const SmNode *pTree = GetDoc()->GetFormulaTree();
+ if (!pTree)
+ return true;
+
+ SmEditWindow* pEdit = GetView().GetEditWindow();
+
+ if (SmViewShell::IsInlineEditEnabled()) {
+ GetCursor().MoveTo(&rDevice, aPos, !rMEvt.IsShift());
+ GetView().InvalidateSlots();
+ // 'on grab' window events are missing in lok, do it explicitly
+ if (comphelper::LibreOfficeKit::isActive())
+ SetIsCursorVisible(true);
+ 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;
+
+ 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 = GetOutputDevice();
+ Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
+ GetCursor().MoveTo(&rDevice, aPos, false);
+
+ CaretBlinkStop();
+ SetIsCursorVisible(true);
+ CaretBlinkStart();
+ RepaintViewShellDoc();
+ }
+ return true;
+}
+
+void SmGraphicWidget::GetFocus()
+{
+ if (!SmViewShell::IsInlineEditEnabled())
+ return;
+ if (SmEditWindow* pEdit = GetView().GetEditWindow())
+ pEdit->Flush();
+ 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()
+{
+ if (SmDocShell* pDoc = GetDoc())
+ pDoc->Repaint();
+}
+
+IMPL_LINK_NOARG(SmGraphicWidget, CaretBlinkTimerHdl, Timer *, void)
+{
+ if (IsCursorVisible())
+ SetIsCursorVisible(false);
+ else
+ SetIsCursorVisible(true);
+
+ RepaintViewShellDoc();
+}
+
+void SmGraphicWidget::CaretBlinkInit()
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return; // No blinking in lok case
+ aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWidget, CaretBlinkTimerHdl));
+ aCaretBlinkTimer.SetTimeout(Application::GetSettings().GetStyleSettings().GetCursorBlinkTime());
+}
+
+void SmGraphicWidget::CaretBlinkStart()
+{
+ if (!SmViewShell::IsInlineEditEnabled() || comphelper::LibreOfficeKit::isActive())
+ return;
+ if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME)
+ aCaretBlinkTimer.Start();
+}
+
+void SmGraphicWidget::CaretBlinkStop()
+{
+ if (!SmViewShell::IsInlineEditEnabled() || comphelper::LibreOfficeKit::isActive())
+ 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)
+ InvertFocusRect(GetOutputDevice(), aCursorRect);
+
+ SetIsCursorVisible(bShow);
+}
+
+void SmGraphicWidget::ShowLine(bool bShow)
+{
+ if (!SmViewShell::IsInlineEditEnabled())
+ return;
+
+ bIsLineVisible = bShow;
+}
+
+void SmGraphicWidget::SetIsCursorVisible(bool bVis)
+{
+ bIsCursorVisible = bVis;
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ mrViewShell.SendCaretToLOK();
+ mrViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE,
+ OString::boolean(bVis));
+ }
+}
+
+void SmGraphicWidget::SetCursor(const SmNode *pNode)
+{
+ if (SmViewShell::IsInlineEditEnabled())
+ return;
+
+ const SmNode *pTree = 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 = 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&)
+{
+ assert(GetDoc());
+ SmDocShell& rDoc = *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 (rDoc.HasCursor() && IsLineVisible())
+ rDoc.GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible());
+ }
+ else
+ {
+ SetIsCursorVisible(false); // (old) cursor must be drawn again
+
+ if (const SmEditWindow* pEdit = GetView().GetEditWindow())
+ { // 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()
+{
+ assert(GetDoc());
+ OutputDevice& rDevice = GetOutputDevice();
+ const Size aTmp(rDevice.PixelToLogic(rDevice.LogicToPixel(GetDoc()->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 (rKEvt.GetKeyCode().GetCode() == KEY_F1)
+ {
+ GetView().StartMainHelp();
+ return true;
+ }
+
+ if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)
+ {
+ // Terminate possible InPlace mode
+ return GetView().Escape();
+ }
+
+ if (!SmViewShell::IsInlineEditEnabled())
+ return GetView().KeyInput(rKEvt);
+
+ bool bConsumed = true;
+
+ SmCursor& rCursor = GetCursor();
+ switch (rKEvt.GetKeyCode().GetFunction())
+ {
+ case KeyFuncType::COPY:
+ rCursor.Copy(&mrGraphicWindow);
+ break;
+ case KeyFuncType::CUT:
+ rCursor.Cut(&mrGraphicWindow);
+ break;
+ case KeyFuncType::PASTE:
+ rCursor.Paste(&mrGraphicWindow);
+ break;
+ case KeyFuncType::UNDO:
+ GetDoc()->Execute(o3tl::temporary(SfxRequest(*GetView().GetFrame(), SID_UNDO)));
+ break;
+ case KeyFuncType::REDO:
+ GetDoc()->Execute(o3tl::temporary(SfxRequest(*GetView().GetFrame(), SID_REDO)));
+ break;
+ default:
+ switch (rKEvt.GetKeyCode().GetCode())
+ {
+ case KEY_LEFT:
+ rCursor.Move(&GetOutputDevice(), MoveLeft, !rKEvt.GetKeyCode().IsShift());
+ break;
+ case KEY_RIGHT:
+ rCursor.Move(&GetOutputDevice(), MoveRight, !rKEvt.GetKeyCode().IsShift());
+ break;
+ case KEY_UP:
+ rCursor.Move(&GetOutputDevice(), MoveUp, !rKEvt.GetKeyCode().IsShift());
+ break;
+ case KEY_DOWN:
+ rCursor.Move(&GetOutputDevice(), MoveDown, !rKEvt.GetKeyCode().IsShift());
+ break;
+ case KEY_RETURN:
+ if (!rKEvt.GetKeyCode().IsShift())
+ rCursor.InsertRow();
+ break;
+ case KEY_DELETE:
+ if (!rCursor.HasSelection())
+ {
+ rCursor.Move(&GetOutputDevice(), MoveRight, false);
+ if (rCursor.HasComplexSelection())
+ break;
+ }
+ rCursor.Delete();
+ break;
+ case KEY_BACKSPACE:
+ rCursor.DeletePrev(&GetOutputDevice());
+ break;
+ default:
+ if (!CharInput(rKEvt.GetCharCode(), rCursor, GetOutputDevice()))
+ bConsumed = GetView().KeyInput(rKEvt);
+ }
+ }
+
+ GetView().InvalidateSlots();
+ CaretBlinkStop();
+ CaretBlinkStart();
+ SetIsCursorVisible(true);
+ RepaintViewShellDoc();
+
+ return bConsumed;
+}
+
+bool SmGraphicWidget::Command(const CommandEvent& rCEvt)
+{
+ bool bCallBase = true;
+ if (!GetView().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;
+ }
+ case CommandEventId::GestureZoom:
+ {
+ const CommandGestureZoomData* pData = rCEvt.GetGestureZoomData();
+ if (pData)
+ {
+ if (pData->meEventType == GestureEventZoomType::Begin)
+ {
+ mfLastZoomScale = pData->mfScaleDelta;
+ }
+ else if (pData->meEventType == GestureEventZoomType::Update)
+ {
+ double deltaBetweenEvents = (pData->mfScaleDelta - mfLastZoomScale) / mfLastZoomScale;
+ mfLastZoomScale = pData->mfScaleDelta;
+
+ // Accumulate fractional zoom to avoid small zoom changes from being ignored
+ mfAccumulatedZoom += deltaBetweenEvents;
+ int nZoomChangePercent = mfAccumulatedZoom * 100;
+ mfAccumulatedZoom -= nZoomChangePercent / 100.0;
+
+ sal_uInt16 nZoom = mrGraphicWindow.GetZoom();
+ nZoom += nZoomChangePercent;
+ mrGraphicWindow.SetZoom(nZoom);
+ }
+ bCallBase = false;
+ }
+ break;
+ }
+
+ default: break;
+ }
+ }
+
+ switch (rCEvt.GetCommand())
+ {
+ case CommandEventId::ExtTextInput:
+ if (SmViewShell::IsInlineEditEnabled())
+ {
+ const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
+ assert(pData);
+ const OUString& rText = pData->GetText();
+ SmCursor& rCursor = GetCursor();
+ OutputDevice& rDevice = GetOutputDevice();
+ 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)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+ nZoom = std::clamp(Factor, MINZOOM, MAXZOOM);
+ Fraction aFraction(nZoom, 100);
+ SetGraphicMapMode(MapMode(SmMapUnit(), 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()
+{
+ // set defined mapmode before calling 'LogicToPixel' below
+ SetGraphicMapMode(MapMode(SmMapUnit()));
+
+ assert(mxGraphic->GetDoc());
+ Size aSize(mxGraphic->GetOutputDevice().LogicToPixel(mxGraphic->GetDoc()->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 inline edit mode
+ if (!SmViewShell::IsInlineEditEnabled())
+ {
+ 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(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(SmMapUnit()));
+ 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 );
+}
+
+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;
+}
+
+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 );
+ if (SmEditWindow* pEdit = GetEditWindow())
+ pEdit->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 ?
+ rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(pDoc->GetModel().get()));
+ SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
+ bRet = ERRCODE_NONE == aEquation.Import(rMedium);
+ }
+ }
+
+ if (!bRet)
+ return;
+
+ OUString aText = pDoc->GetText();
+ if (SmEditWindow *pEditWin = GetEditWindow())
+ 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 )
+ {
+ rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(pDoc->GetModel().get()));
+ SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
+ bSuccess = ERRCODE_NONE == aEquation.Import(rMedium);
+ }
+ }
+
+ if (!bSuccess)
+ return;
+
+ OUString aText = pDoc->GetText();
+ if (SmEditWindow *pEditWin = GetEditWindow())
+ 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 = dynamic_cast<TransferableHelper*>(xTrans.get());
+ if (pTrans)
+ {
+ if (pWin)
+ pTrans->CopyToClipboard(pWin->GetClipboard());
+ }
+ }
+ }
+ break;
+
+ case SID_PASTEOBJECT:
+ {
+ uno::Reference < io::XInputStream > xStrm;
+ if (pWin)
+ {
+ TransferableDataHelper aData(TransferableDataHelper::CreateFromClipboard(pWin->GetClipboard()));
+ 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 (IsInlineEditEnabled())
+ {
+ GetDoc()->GetCursor().Cut(&GetGraphicWindow());
+ GetGraphicWidget().GrabFocus();
+ }
+ else if (pWin)
+ pWin->Cut();
+ break;
+
+ case SID_COPY:
+ if (IsInlineEditEnabled())
+ {
+ GetDoc()->GetCursor().Copy(&GetGraphicWindow());
+ GetGraphicWidget().GrabFocus();
+ }
+ else if (pWin)
+ {
+ if (pWin->IsAllSelected())
+ {
+ GetViewFrame().GetDispatcher()->ExecuteList(
+ SID_COPYOBJECT, SfxCallMode::RECORD,
+ { new SfxVoidItem(SID_COPYOBJECT) });
+ }
+ else
+ pWin->Copy();
+ }
+ break;
+
+ case SID_PASTE:
+ {
+ if (IsInlineEditEnabled())
+ {
+ GetDoc()->GetCursor().Paste(&GetGraphicWindow());
+ GetGraphicWidget().GrabFocus();
+ break;
+ }
+
+ bool bCallExec = nullptr == pWin;
+ if( !bCallExec )
+ {
+ if (pWin)
+ {
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromClipboard(
+ pWin->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 (IsInlineEditEnabled())
+ {
+ if (!GetDoc()->GetCursor().HasSelection())
+ {
+ GetDoc()->GetCursor().Move(&GetGraphicWindow().GetGraphicWidget().GetOutputDevice(), MoveRight, false);
+ if (!GetDoc()->GetCursor().HasComplexSelection())
+ GetDoc()->GetCursor().Delete();
+ }
+ else
+ GetDoc()->GetCursor().Delete();
+ GetGraphicWidget().GrabFocus();
+ }
+ else if (pWin)
+ pWin->Delete();
+ break;
+
+ case SID_SELECT:
+ if (pWin)
+ pWin->SelectAll();
+ break;
+
+ case SID_INSERTCOMMANDTEXT:
+ {
+ const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_INSERTCOMMANDTEXT);
+
+ if (IsInlineEditEnabled())
+ {
+ GetDoc()->GetCursor().InsertCommandText(rItem.GetValue());
+ GetGraphicWidget().GrabFocus();
+ }
+ else if (pWin)
+ {
+ pWin->InsertText(rItem.GetValue());
+ }
+ break;
+
+ }
+
+ case SID_INSERTSPECIAL:
+ {
+ const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_INSERTSPECIAL);
+
+ if (IsInlineEditEnabled())
+ GetDoc()->GetCursor().InsertSpecial(rItem.GetValue());
+ else if (pWin)
+ pWin->InsertText(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:
+ {
+ if (pWin)
+ {
+ TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(pWin->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 = 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:
+ {
+ // First make sure that the sidebar is visible
+ GetViewFrame().ShowChildWindow(SID_SIDEBAR);
+
+ sfx2::sidebar::Sidebar::TogglePanel(u"MathElementsPanel",
+ GetViewFrame().GetFrame().GetFrameInterface());
+ GetViewFrame().GetBindings().Invalidate( SID_ELEMENTSDOCKINGWINDOW );
+
+ rReq.Ignore ();
+ }
+ break;
+
+ case SID_CMDBOXWINDOW:
+ {
+ GetViewFrame().ToggleChildWindow(SID_CMDBOXWINDOW);
+ GetViewFrame().GetBindings().Invalidate(SID_CMDBOXWINDOW);
+ }
+ 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;
+
+ case SID_CHARMAP:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const SfxStringItem* pItem = nullptr;
+ if (pArgs && SfxItemState::SET == pArgs->GetItemState(SID_CHARMAP, true, &pItem))
+ {
+ if (IsInlineEditEnabled())
+ GetDoc()->GetCursor().InsertText(pItem->GetValue());
+ else if (pWin)
+ pWin->InsertText(pItem->GetValue());
+ break;
+ }
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ SfxAllItemSet aSet(GetViewFrame().GetObjectShell()->GetPool());
+ aSet.Put(SfxBoolItem(FN_PARAM_1, false));
+ aSet.Put(SfxStringItem(SID_FONT_NAME,
+ GetDoc()->GetFormat().GetFont(FNT_VARIABLE).GetFamilyName()));
+ ScopedVclPtr<SfxAbstractDialog> pDialog(
+ pFact->CreateCharMapDialog(pWin ? pWin->GetFrameWeld() : nullptr, aSet,
+ GetViewFrame().GetFrame().GetFrameInterface()));
+ pDialog->Execute();
+ }
+ break;
+
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ {
+ bool bRTL = rReq.GetSlot() == SID_ATTR_PARA_RIGHT_TO_LEFT;
+ GetDoc()->SetRightToLeft(bRTL);
+ GetGraphicWindow().GetGraphicWidget().GetOutputDevice().EnableRTL(bRTL);
+ GetViewFrame().GetBindings().Invalidate(bRTL ? SID_ATTR_PARA_LEFT_TO_RIGHT : SID_ATTR_PARA_RIGHT_TO_LEFT);
+ }
+ 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 (IsInlineEditEnabled())
+ {
+ if (!GetDoc()->GetCursor().HasSelection())
+ rSet.DisableItem(nWh);
+ }
+ else 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:
+ {
+ if (IsInlineEditEnabled())
+ rSet.DisableItem(nWh);
+ else
+ rSet.Put(SfxBoolItem(nWh, SM_MOD()->GetConfig()->IsShowFormulaCursor()));
+ }
+ break;
+ case SID_ELEMENTSDOCKINGWINDOW:
+ {
+ const bool bState = sfx2::sidebar::Sidebar::IsPanelVisible(
+ u"MathElementsPanel", GetViewFrame().GetFrame().GetFrameInterface());
+ rSet.Put(SfxBoolItem(SID_ELEMENTSDOCKINGWINDOW, bState));
+ }
+ break;
+ case SID_CMDBOXWINDOW:
+ {
+ bool bState = false;
+ auto pCmdWin = GetViewFrame().GetChildWindow(SID_CMDBOXWINDOW);
+ if (pCmdWin)
+ bState = pCmdWin->IsVisible();
+ rSet.Put(SfxBoolItem(SID_CMDBOXWINDOW, bState));
+ }
+ break;
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ rSet.Put(SfxBoolItem(nWh, !GetDoc()->GetFormat().IsRightToLeft()));
+ break;
+
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ rSet.Put(SfxBoolItem(nWh, GetDoc()->GetFormat().IsRightToLeft()));
+ break;
+ }
+ }
+}
+
+namespace
+{
+css::uno::Reference<css::ui::XSidebar>
+getSidebarFromModel(const css::uno::Reference<css::frame::XModel>& xModel)
+{
+ css::uno::Reference<css::container::XChild> xChild(xModel, css::uno::UNO_QUERY);
+ if (!xChild.is())
+ return nullptr;
+ css::uno::Reference<css::frame::XModel> xParent(xChild->getParent(), css::uno::UNO_QUERY);
+ if (!xParent.is())
+ return nullptr;
+ css::uno::Reference<css::frame::XController2> xController(xParent->getCurrentController(),
+ css::uno::UNO_QUERY);
+ if (!xController.is())
+ return nullptr;
+ css::uno::Reference<css::ui::XSidebarProvider> xSidebarProvider = xController->getSidebar();
+ if (!xSidebarProvider.is())
+ return nullptr;
+ return xSidebarProvider->getSidebar();
+}
+class SmController : public SfxBaseController
+{
+public:
+ SmController(SfxViewShell& rViewShell)
+ : SfxBaseController(&rViewShell)
+ , mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler(
+ GetContextName, this, vcl::EnumContext::Context::Math))
+ {
+ rViewShell.SetContextName(GetContextName());
+ }
+ // No need to call mpSelectionChangeHandler->Disconnect() unless SmController implements XSelectionSupplier
+ // ~SmController() { mpSelectionChangeHandler->Disconnect(); }
+
+ // css::frame::XController
+ void SAL_CALL attachFrame(const css::uno::Reference<css::frame::XFrame>& xFrame) override
+ {
+ SfxBaseController::attachFrame(xFrame);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ CopyLokViewCallbackFromFrameCreator();
+ // In lok mode, DocumentHolder::ShowUI is not called on OLE in-place activation,
+ // because respective code is skipped in OCommonEmbeddedObject::SwitchStateTo_Impl,
+ // so sidebar controller does not get registered properly; do it here
+ if (auto xSidebar = getSidebarFromModel(getModel()))
+ {
+ auto pSidebar = dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get());
+ assert(pSidebar);
+ pSidebar->registerSidebarForFrame(this);
+ pSidebar->updateModel(getModel());
+ }
+ }
+
+ // No need to call mpSelectionChangeHandler->Connect() unless SmController implements XSelectionSupplier
+ mpSelectionChangeHandler->selectionChanged({}); // Installs the correct context
+ }
+
+ virtual void SAL_CALL dispose() override
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ if (auto pViewShell = GetViewShell_Impl())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE,
+ OString::boolean(false));
+
+ SfxBaseController::dispose();
+ }
+
+private:
+ static OUString GetContextName() { return "Math"; } // Static constant for now
+
+ rtl::Reference<svx::sidebar::SelectionChangeHandler> mpSelectionChangeHandler;
+};
+}
+
+SmViewShell::SmViewShell(SfxViewFrame& rFrame_, SfxViewShell *)
+ : SfxViewShell(rFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS)
+ , mxGraphicWindow(VclPtr<SmGraphicWindow>::Create(*this))
+ , maGraphicController(mxGraphicWindow->GetGraphicWidget(), SID_GRAPHIC_SM, rFrame_.GetBindings())
+ , mbPasteState(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
+ if (SmEditWindow *pEditWin = GetEditWindow())
+ pEditWin->DeleteEditView();
+ mxGraphicWindow.disposeAndClear();
+}
+
+void SmViewShell::Deactivate( bool bIsMDIActivate )
+{
+ if (SmEditWindow *pEdit = GetEditWindow())
+ pEdit->Flush();
+
+ SfxViewShell::Deactivate( bIsMDIActivate );
+}
+
+void SmViewShell::Activate( bool bIsMDIActivate )
+{
+ SfxViewShell::Activate( bIsMDIActivate );
+
+ if (IsInlineEditEnabled())
+ {
+ // 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()
+ || SM_MOD()->GetConfig()->IsInlineEditEnable();
+}
+
+void SmViewShell::StartMainHelp()
+{
+ Help* pHelp = Application::GetHelp();
+ if (pHelp)
+ pHelp->Start(HID_SMA_MAIN_HELP, GetViewFrame().GetFrameWeld());
+}
+
+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( SmMapUnit() );
+ 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));
+ if (GraphicSize.Width() <= 0 || GraphicSize.Height() <= 0)
+ break;
+ sal_uInt16 nZ = std::min(o3tl::convert(OutputSize.Width(), 100, GraphicSize.Width()),
+ o3tl::convert(OutputSize.Height(), 100, GraphicSize.Height()));
+ mxGraphicWindow->SetZoom(nZ);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+std::optional<OString> SmViewShell::getLOKPayload(int nType, int nViewId) const
+{
+ switch (nType)
+ {
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ {
+ OString sRectangle;
+ if (const SmGraphicWidget& widget = GetGraphicWidget(); widget.IsCursorVisible())
+ {
+ SmCursor& rCursor = GetDoc()->GetCursor();
+ OutputDevice& rOutDev = const_cast<SmGraphicWidget&>(widget).GetOutputDevice();
+ tools::Rectangle aCaret = rCursor.GetCaretRectangle(rOutDev);
+ Point aFormulaDrawPos = widget.GetFormulaDrawPos();
+ aCaret.Move(aFormulaDrawPos.X(), aFormulaDrawPos.Y());
+ LokStarMathHelper helper(SfxViewShell::Current());
+ tools::Rectangle aBounds = helper.GetBoundingBox();
+ aCaret.Move(aBounds.Left(), aBounds.Top());
+ sRectangle = aCaret.toString();
+ }
+ return SfxLokHelper::makeVisCursorInvalidation(nViewId, sRectangle, false, {});
+ }
+ case LOK_CALLBACK_TEXT_SELECTION:
+ {
+ OString sRectangle;
+ if (const SmGraphicWidget& widget = GetGraphicWidget(); widget.IsCursorVisible())
+ {
+ SmCursor& rCursor = GetDoc()->GetCursor();
+ OutputDevice& rOutDev = const_cast<SmGraphicWidget&>(widget).GetOutputDevice();
+ tools::Rectangle aSelection = rCursor.GetSelectionRectangle(rOutDev);
+ if (!aSelection.IsEmpty())
+ {
+ Point aFormulaDrawPos = widget.GetFormulaDrawPos();
+ aSelection.Move(aFormulaDrawPos.X(), aFormulaDrawPos.Y());
+ LokStarMathHelper helper(SfxViewShell::Current());
+ tools::Rectangle aBounds = helper.GetBoundingBox();
+
+ aSelection.Move(aBounds.Left(), aBounds.Top());
+ sRectangle = aSelection.toString();
+ }
+ }
+ return sRectangle;
+ }
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
+ case LOK_CALLBACK_TEXT_VIEW_SELECTION:
+ return {};
+ }
+ return SfxViewShell::getLOKPayload(nType, nViewId); // aborts
+}
+
+void SmViewShell::SendCaretToLOK() const
+{
+ const int nViewId = sal_Int32(GetViewShellId());
+ if (const auto& payload = getLOKPayload(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, nViewId))
+ {
+ libreOfficeKitViewCallbackWithViewId(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR,
+ *payload, nViewId);
+ }
+ if (const auto& payload = getLOKPayload(LOK_CALLBACK_TEXT_SELECTION, nViewId))
+ {
+ libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, *payload);
+ }
+}
+
+void SmViewShell::InvalidateSlots()
+{
+ auto& rBind = GetViewFrame().GetBindings();
+ rBind.Invalidate(SID_COPY);
+ rBind.Invalidate(SID_CUT);
+ rBind.Invalidate(SID_DELETE);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/visitors.cxx b/starmath/source/visitors.cxx
new file mode 100644
index 0000000000..3e11201ebe
--- /dev/null
+++ b/starmath/source/visitors.cxx
@@ -0,0 +1,2648 @@
+/* -*- 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 );
+}
+
+// SmCaretLinesVisitor
+
+SmCaretLinesVisitor::SmCaretLinesVisitor(OutputDevice& rDevice, SmCaretPos position, Point offset)
+ : mrDev(rDevice)
+ , maPos(position)
+ , maOffset(offset)
+{
+}
+
+void SmCaretLinesVisitor::DoIt()
+{
+ SAL_WARN_IF(!maPos.IsValid(), "starmath", "Cannot draw invalid position!");
+ if (!maPos.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 SmCaretLinesVisitor::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( );
+
+ // Vertical line
+ ProcessCaretLine({ left, top }, { left, top + height });
+
+ // Underline
+ ProcessUnderline({ left_line, top + height }, { right_line, top + height });
+}
+
+void SmCaretLinesVisitor::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( );
+
+ // Vertical line
+ ProcessCaretLine({ left, top }, { left, top + height });
+
+ // Underline
+ ProcessUnderline({ left_line, top + height }, { right_line, top + height });
+}
+
+// SmCaretRectanglesVisitor
+
+SmCaretRectanglesVisitor::SmCaretRectanglesVisitor(OutputDevice& rDevice, SmCaretPos position)
+ : SmCaretLinesVisitor(rDevice, position, {})
+{
+ DoIt();
+}
+
+void SmCaretRectanglesVisitor::ProcessCaretLine(Point from, Point to) { maCaret = { from, to }; }
+void SmCaretRectanglesVisitor::ProcessUnderline(Point /*from*/, Point /*to*/) {} // No underline
+
+// SmCaretDrawingVisitor
+
+SmCaretDrawingVisitor::SmCaretDrawingVisitor( OutputDevice& rDevice,
+ SmCaretPos position,
+ Point offset,
+ bool caretVisible )
+ : SmCaretLinesVisitor(rDevice, position, offset)
+ , mbCaretVisible( caretVisible )
+{
+ DoIt();
+}
+
+void SmCaretDrawingVisitor::ProcessCaretLine(Point from, Point to)
+{
+ if ( mbCaretVisible ) {
+ //Set color
+ getDev().SetLineColor(COL_BLACK);
+ //Draw vertical line
+ getDev().DrawLine(from, to);
+ }
+}
+
+void SmCaretDrawingVisitor::ProcessUnderline(Point from, Point to)
+{
+ //Set color
+ getDev().SetLineColor(COL_BLACK);
+ //Underline the line
+ getDev().DrawLine(from, to);
+}
+
+// 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 ) );
+
+ if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
+ mrDev.ReMirror(aBar);
+
+ 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 );
+
+ if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
+ mrDev.ReMirror(aPos);
+
+ 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" );
+
+ if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
+ mrDev.ReMirror(aTmp);
+
+ 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( ) );
+
+ if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
+ mrDev.ReMirror(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 );
+
+ SmNode* pChild;
+ for (SmSubSup const nodeType : { LSUP, LSUB, CSUP, CSUB, RSUP, RSUB })
+ {
+ pChild = pNode->GetSubSup(nodeType);
+ if( pChild )
+ {
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), ((nodeType == RSUP) || (nodeType == RSUB))?bodyRight:left );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( ((nodeType == LSUP) || (nodeType == LSUB))?bodyLeft: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;
+ for (SmSubSup const nodeType : { LSUP, LSUB, CSUP, CSUB, RSUP, RSUB })
+ {
+ pChild = pSubSup->GetSubSup(nodeType);
+ 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" );
+
+ OUString& aText = pNode->GetText();
+ sal_Int32 i = 0;
+ while (i < aText.getLength())
+ {
+ aText.iterateCodePoints(&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 )
+ : SmSelectionRectanglesVisitor( rDevice, pTree )
+{
+ //Draw selection if there's any
+ if(GetSelection().IsEmpty()) return;
+
+ tools::Rectangle aSelectionArea = GetSelection() + rOffset;
+
+ //Save device state
+ rDevice.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+ //Change colors
+ rDevice.SetLineColor( );
+ rDevice.SetFillColor( COL_LIGHTGRAY );
+
+ //Draw rectangle
+ rDevice.DrawRect( aSelectionArea );
+
+ //Restore device state
+ rDevice.Pop( );
+}
+
+// SmSelectionRectanglesVisitor
+
+SmSelectionRectanglesVisitor::SmSelectionRectanglesVisitor(OutputDevice& rDevice, SmNode* pTree)
+ : mrDev(rDevice)
+{
+ // Visit everything
+ SAL_WARN_IF(!pTree, "starmath", "pTree can't be null!");
+ if (pTree)
+ pTree->Accept(this);
+}
+
+void SmSelectionRectanglesVisitor::DefaultVisit( SmNode* pNode )
+{
+ if( pNode->IsSelected( ) )
+ ExtendSelectionArea( pNode->AsRectangle( ) );
+ VisitChildren( pNode );
+}
+
+void SmSelectionRectanglesVisitor::VisitChildren( SmNode* pNode )
+{
+ if(pNode->GetNumSubNodes() == 0)
+ return;
+ for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+void SmSelectionRectanglesVisitor::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;
+ 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* )
+{
+ // Add something for error nodes so that we can parse this back.
+ Append(u"{}");
+}
+
+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;
+ 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 0000000000..adf1a275c3
--- /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 0000000000..33a179d05f
--- /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: */