Files
ElyOffice/patches/libreoffice/0051-writer-eq-fields-as-formulas.patch
T
ZacharyZhang-NYandClaude 2ca53ef585 R8.2: Word EQ fields open as formulas; inline objects follow their run
Gitea #9. Patch 0049 moves inline pictures and OLE objects with
their run's w:position. Patches 0050 and 0051 turn EQ fields into
Math objects; unreadable fields show their code. Round 3 findings
(lowercase \s\do tail, split subscripts, \ai/\di, italics through
OMML) fixed and covered by word.rs; no fourth review.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 21:39:17 -04:00

170 lines
7.5 KiB
Diff

Writer: Word EQ fields open as formulas
DOCX import read only EQ's \o, \s\do and ruby forms; every other
EQ field, such as \f(a,b) for a fraction, vanished (Gitea #9).
The import now turns an EQ field into a Math object through
the parser of the previous patch. Each command character keeps
its run's italic and vertAlign, as Word draws them (a with a
subscript 10 is a_{10}); the formula takes the field's font
size. A command the parser cannot read shows its field code as
text, so nothing vanishes and the failure stays visible. Only a
whole plain \s\do N(text) field stays lowered text, as before;
the old reader dropped anything after its first bracket.
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx
@@ -26,6 +26,7 @@
#include <com/sun/star/style/TabStop.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <queue>
#include <stack>
#include <string_view>
@@ -50,6 +51,7 @@
#include "ThemeHandler.hxx"
#include "GraphicImport.hxx"
#include "OLEHandler.hxx"
+#include "EqField.hxx"
#include "FFDataHandler.hxx"
#include "SmartTagHandler.hxx"
#include "FormControlHelper.hxx"
@@ -303,6 +305,8 @@ class FieldContext : public virtual SvRefBase
// 0: Normal, inserted command line
// 1: Deleted command line
OUString m_sCommand[2];
+ /// The look of each inserted command character, for EQ fields.
+ std::vector<EqLook> m_aCommandLooks;
OUString m_sResult;
OUString m_sVariableValue;
std::optional<FieldId> m_eFieldId;
@@ -346,6 +350,12 @@ public:
void AppendCommand(std::u16string_view rPart);
/// Deleted command text has no look.
+ void AppendLooks(sal_Int32 nCount, EqLook aLook)
+ {
+ if (!m_bCommandType)
+ m_aCommandLooks.insert(m_aCommandLooks.end(), nCount, aLook);
+ }
+ const std::vector<EqLook>& GetCommandLooks() const { return m_aCommandLooks; }
const OUString& GetCommand() const {return m_sCommand[m_bCommandType]; }
bool GetCommandIsEmpty(bool bType) const { return m_sCommand[bType].isEmpty(); }
void SetCommandType(bool cType) { m_bCommandType = cType; }
@@ -846,6 +856,9 @@ public:
void moveWithRun(const css::uno::Reference<css::text::XTextContent>& xContent);
void appendOLE( const OUString& rStreamName, const std::shared_ptr<OLEHandler>& pOleHandler );
void appendStarMath( const Value& v);
+ void appendFormula(const css::uno::Reference<css::embed::XEmbeddedObject>& xFormula);
+ /// A Word EQ field as a formula object.
+ void appendEqFormula(const FieldContextPtr& pContext);
void adjustLastPara(sal_Int8 nAlign);
rtl::Reference<SwXTextSection> appendTextSectionAfter(css::uno::Reference<css::text::XTextRange> const & xBefore);
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -92,6 +92,9 @@
#include <com/sun/star/document/XImporter.hpp>
#include <com/sun/star/document/XFilter.hpp>
#include <comphelper/indexedpropertyvalues.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/embeddedobjectcontainer.hxx>
+#include <tools/globname.hxx>
#include <editeng/flditem.hxx>
#include <editeng/unotext.hxx>
#include <o3tl/deleter.hxx>
@@ -3815,19 +3818,44 @@ void DomainMapper_Impl::appendOLE( const OUString& rStreamName, const std::share
}
+void DomainMapper_Impl::appendEqFormula(const FieldContextPtr& pContext)
+{
+ // the looks index the untrimmed command
+ const OUString aMath = EqToStarMath(pContext->GetCommand(), pContext->GetCommandLooks());
+ if (aMath.isEmpty())
+ {
+ // an unreadable field shows its code, never vanishes
+ appendTextPortion(pContext->GetCommand().trim(), pContext->getProperties());
+ return;
+ }
+ comphelper::EmbeddedObjectContainer aContainer;
+ OUString aName;
+ uno::Reference<embed::XEmbeddedObject> xFormula = aContainer.CreateEmbeddedObject(
+ SvGlobalName(SO3_SM_CLASSID).GetByteSequence(), aName, m_aBaseUrl);
+ uno::Reference<beans::XPropertySet> xMath(xFormula->getComponent(), uno::UNO_QUERY_THROW);
+ xMath->setPropertyValue(u"Formula"_ustr, uno::Any(aMath));
+ double fHeight = 0;
+ if (GetAnyProperty(PROP_CHAR_HEIGHT, pContext->getProperties()) >>= fHeight)
+ xMath->setPropertyValue(u"BaseFontHeight"_ustr, uno::Any(sal_Int16(std::round(fHeight))));
+ appendFormula(xFormula);
+}
+
void DomainMapper_Impl::appendStarMath( const Value& val )
{
uno::Reference< embed::XEmbeddedObject > formula;
val.getAny() >>= formula;
- if( !formula.is() )
- return;
+ if( formula.is() )
+ appendFormula(formula);
+}
+void DomainMapper_Impl::appendFormula(const uno::Reference<embed::XEmbeddedObject>& formula)
+{
try
{
rtl::Reference<SwXTextEmbeddedObject> xStarMath = m_xTextDocument->createTextEmbeddedObject();
xStarMath->setPropertyValue(getPropertyName( PROP_EMBEDDED_OBJECT ),
- val.getAny());
+ uno::Any(formula));
// tdf#66405: set zero margins for embedded object
xStarMath->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ),
uno::Any(sal_Int32(0)));
@@ -6599,11 +6627,20 @@ void DomainMapper_Impl::AppendFieldCommand(OUString const & rPartOfCommand)
{
// Set command line type: normal or deleted
pContext->SetCommandType(m_bTextDeleted);
+ PropertyMapPtr pCharContext = GetTopContextOfType(CONTEXT_CHARACTER);
if (pContext->GetCommand().isEmpty())
{
- pContext->getProperties()->InsertProps(GetTopContextOfType(CONTEXT_CHARACTER));
+ pContext->getProperties()->InsertProps(pCharContext);
}
pContext->AppendCommand( rPartOfCommand );
+ EqLook aLook;
+ awt::FontSlant eSlant = awt::FontSlant_NONE;
+ aLook.bItalic = (GetAnyProperty(PROP_CHAR_POSTURE, pCharContext) >>= eSlant)
+ && eSlant == awt::FontSlant_ITALIC;
+ sal_Int16 nEscapement = 0;
+ GetAnyProperty(PROP_CHAR_ESCAPEMENT, pCharContext) >>= nEscapement;
+ aLook.nScript = nEscapement > 0 ? 1 : nEscapement < 0 ? -1 : 0;
+ pContext->AppendLooks(rPartOfCommand.getLength(), aLook);
}
}
@@ -8209,7 +8246,13 @@ void DomainMapper_Impl::CloseFieldCommand()
sal_Int32 nSpaceIndex = aCommand.indexOf(' ');
if(nSpaceIndex > 0)
aCommand = o3tl::trim(aCommand.subView(nSpaceIndex));
- if (aCommand.startsWith("\\s"))
+ // only a whole plain lowered run stays text
+ sal_Int32 nOpen = aCommand.indexOf('(');
+ bool bLowered = aCommand.startsWith("\\s\\do") && nOpen > 0
+ && aCommand.indexOf(')') == aCommand.getLength() - 1
+ && aCommand.indexOf('(', nOpen + 1) < 0
+ && aCommand.indexOf('\\', 5) < 0;
+ if (bLowered)
{
aCommand = aCommand.copy(2);
if (aCommand.startsWith("\\do"))
@@ -8236,6 +8279,8 @@ void DomainMapper_Impl::CloseFieldCommand()
{
handleRubyEQField(pContext);
}
+ else
+ appendEqFormula(pContext);
}
}
break;