cool#16233 sw: keep a formula's font size across DOCX import and export

An equation's size lives in the w:sz of its math runs. The import ignored it,
so every equation came in at the 12pt default of the formula editor, and a file
holding equations at three sizes showed all three the same. An equation whose
runs name no size takes the size of the text around it, which the styles and
the document defaults hold.

The export wrote no size at all. It now puts the size on every math run, and in
the control properties of each object, which is where the fraction bar, the
radical sign and the brackets take theirs from. The size travels in the w
namespace, so it goes out for a DOCX only, the way the color of a formula
already does.

Every formula now carries a size of its own on the way out, including one
written in ODF. Such a formula used to leave the size open and took whatever
the text around it had in the reading application. It now keeps the size it is
drawn at here, so both applications show it the same.

Setting the base size means giving each of the fonts that same size. The font
size dialog, the BaseFontHeight property of the model and this import each did
that on their own, so they now share SmFormat::SetBaseSizeAndFonts. The
property used to put the sizes in a copy that a later merge step reads only for
a font the same call has named, so a lone BaseFontHeight left the fonts alone.

Signed-off-by: Gülşah Köse <gulsah.kose@collabora.com>
Change-Id: I18b6c5814a57d8ccb8be5b7bdd357ce76cea024d
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13297
Reviewed-by: Balázs Varga <balazs.varga@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 582e3fdb6496d9f511dfefc1f716b4ead6cc3797)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211600
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Gülşah Köse
2026-09-30 12:12:58 +02:00
committed by Xisco Fauli
parent 5a201620b2
commit 111a2ffc8b
19 changed files with 245 additions and 72 deletions
+6
View File
@@ -36,6 +36,12 @@ class OOX_DLLPUBLIC SAL_LOPLUGIN_ANNOTATE("crosscast") FormulaImExportBase
public:
virtual void readFormulaOoxml( oox::formulaimport::XmlStream& stream ) = 0;
virtual Size getFormulaSize() const = 0; // Unit is mm100
/** The font size in half points that the OOXML markup gave for the formula. It is 0 when
the markup carried no size, and the formula then has whatever size the text around it
has. */
virtual sal_Int32 getFormulaFontSizeInHalfPoints() const = 0;
/** Draw the formula at the given size, in half points. */
virtual void setFormulaFontSizeInHalfPoints( sal_Int32 nFontSize ) = 0;
virtual void writeFormulaOoxml(::sax_fastparser::FSHelperPtr pSerializer,
oox::core::OoxmlVersion version,
+8
View File
@@ -205,6 +205,14 @@ public:
Handle the current (unexpected) tag.
*/
void handleUnexpectedTag();
/**
Collects an attribute from every opening tag of the given type that sits inside an
element of the given enclosing type, anywhere in the stream and however deeply nested.
The position in the stream stays where it is.
@return the attribute values, in the order the tags carrying them appear
*/
std::vector<OUString> tagAttributesWithin(int nEnclosingToken, int nTagToken,
int nAttributeToken) const;
protected:
Tag checkTag(int token, bool optional);
+17
View File
@@ -312,6 +312,23 @@ void XmlStream::handleUnexpectedTag()
skipElementInternal( currentToken(), false ); // otherwise skip the entire element
}
std::vector<OUString> XmlStream::tagAttributesWithin( int nEnclosingToken, int nTagToken,
int nAttributeToken ) const
{
std::vector<OUString> aValues;
int nDepth = 0;
for( const Tag& rTag : tags )
{
if( rTag.token == OPENING( nEnclosingToken ))
++nDepth;
else if( rTag.token == CLOSING( nEnclosingToken ))
--nDepth;
else if( nDepth > 0 && rTag.token == OPENING( nTagToken ))
aValues.push_back( rTag.attribute( nAttributeToken ));
}
return aValues;
}
void XmlStreamBuilder::appendOpeningTag( int token, const uno::Reference< xml::sax::XFastAttributeList >& attrs )
{
tags.emplace_back( OPENING( token ), attrs );
+8
View File
@@ -93,6 +93,8 @@ class SM_DLLPUBLIC SmDocShell final : public SfxObjectShell, public SfxListener
sal_uInt16 mnModifyCount;
bool mbFormulaArranged;
sal_Int16 mnSmSyntaxVersion;
/// Font size in half points that the OOXML markup gave for the formula, 0 when it gave none.
sal_Int32 mnOoxmlFontSizeInHalfPoints = 0;
std::unique_ptr<AbstractSmParser> maParser;
std::unique_ptr<SmCursor> mpCursor;
std::set< OUString > maUsedSymbols; // to export used symbols only when saving
@@ -215,6 +217,12 @@ public:
const sal_Int8 nAlign);
void writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding);
void readFormulaOoxml( oox::formulaimport::XmlStream& stream );
/** The font size in half points that the OOXML markup gave for the formula, or 0 when the
markup carried no size of its own. */
sal_Int32 GetOoxmlFontSizeInHalfPoints() const { return mnOoxmlFontSizeInHalfPoints; }
/** Draw the formula at the given size, in half points. The size becomes the base size and
the size of each of the fonts. A size of zero or less leaves the formula as it is. */
void SetFontSizeInHalfPoints(sal_Int32 nFontSize);
void UpdateEditEngineDefaultFonts();
+2
View File
@@ -107,6 +107,8 @@ public:
const Size & GetBaseSize() const { return aBaseSize; }
void SetBaseSize(const Size &rSize) { aBaseSize = rSize; }
/// Sets the base size and gives each of the fonts that same size.
void SetBaseSizeAndFonts(const Size &rSize);
const SmFace & GetFont(sal_uInt16 nIdent) const { return vFont[nIdent]; }
void SetFont(sal_uInt16 nIdent, const SmFace &rFont, bool bDefault = false);
+2
View File
@@ -91,6 +91,8 @@ public:
virtual void writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding) override;
virtual void readFormulaOoxml( oox::formulaimport::XmlStream& stream ) override;
virtual Size getFormulaSize() const override;
virtual sal_Int32 getFormulaFontSizeInHalfPoints() const override;
virtual void setFormulaFontSizeInHalfPoints(sal_Int32 nFontSize) override;
private:
OUString msOldWindowState; // imported window state
+1 -5
View File
@@ -521,7 +521,7 @@ void SmFontSizeDialog::ReadFrom(const SmFormat &rFormat)
void SmFontSizeDialog::WriteTo(SmFormat &rFormat) const
{
rFormat.SetBaseSize( Size(0, o3tl::convert(m_xBaseSize->get_value(FieldUnit::NONE), o3tl::Length::pt, SmO3tlLengthUnit())) );
rFormat.SetBaseSizeAndFonts( 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)));
@@ -529,10 +529,6 @@ void SmFontSizeDialog::WriteTo(SmFormat &rFormat) const
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();
}
+16 -1
View File
@@ -835,7 +835,9 @@ void SmDocShell::writeFormulaOoxml(
Parse();
if( mpTree )
ArrangeFormula();
SmOoxmlExport aEquation(mpTree.get(), version, documentType);
SmOoxmlExport aEquation(mpTree.get(), version, documentType,
o3tl::convert(maFormat.GetBaseSize().Height() * 2, SmO3tlLengthUnit(),
o3tl::Length::pt));
if(documentType == oox::drawingml::DOCUMENT_DOCX)
aEquation.ConvertFromStarMath( pSerializer, nAlign);
else
@@ -856,6 +858,19 @@ void SmDocShell::readFormulaOoxml( oox::formulaimport::XmlStream& stream )
{
SmOoxmlImport aEquation( stream );
SetText( aEquation.ConvertToStarMath());
mnOoxmlFontSizeInHalfPoints = aEquation.GetFontSizeInHalfPoints();
SetFontSizeInHalfPoints(mnOoxmlFontSizeInHalfPoints);
}
void SmDocShell::SetFontSizeInHalfPoints(sal_Int32 nFontSize)
{
if (nFontSize <= 0)
return;
SmFormat aFormat(GetFormat());
Size aSize(aFormat.GetBaseSize());
aSize.setHeight(o3tl::convert(nFontSize, o3tl::Length::pt, SmO3tlLengthUnit()) / 2);
aFormat.SetBaseSizeAndFonts(aSize);
SetFormat(aFormat);
}
void SmDocShell::Execute(SfxRequest& rReq)
+7
View File
@@ -89,6 +89,13 @@ SmFormat::SmFormat()
}
void SmFormat::SetBaseSizeAndFonts(const Size &rSize)
{
SetBaseSize(rSize);
for (sal_uInt16 nFont = FNT_BEGIN; nFont <= FNT_END; ++nFont)
SetFontSize(nFont, rSize);
}
void SmFormat::SetFont(sal_uInt16 nIdent, const SmFace &rFont, bool bDefault )
{
vFont[nIdent] = rFont;
+70 -45
View File
@@ -22,10 +22,11 @@ using namespace oox;
using namespace oox::core;
SmOoxmlExport::SmOoxmlExport(const SmNode *const pIn, OoxmlVersion const v,
drawingml::DocumentType const documentType)
drawingml::DocumentType const documentType, sal_Int32 const nFontSizeInHalfPoints)
: SmWordExportBase( pIn )
, version( v )
, m_DocumentType(documentType)
, m_nFontSizeInHalfPoints(nFontSizeInHalfPoints)
{
}
@@ -84,27 +85,33 @@ void SmOoxmlExport::ConvertFromStarMath( const ::sax_fastparser::FSHelperPtr& se
// part of the ooxml math stuff.
// The w prefix is bound in a DOCX and nowhere else. A formula in a presentation or a
// spreadsheet goes out inside a slide or a sheet, which never declare it, so a color
// there would leave the prefix unbound.
bool SmOoxmlExport::HasOwnColor( const SmNode* pNode ) const
// spreadsheet goes out inside a slide or a sheet, which never declare it, so run
// properties there would leave the prefix unbound.
bool SmOoxmlExport::WritesRunProperties() const
{
return drawingml::DOCUMENT_DOCX == m_DocumentType
&& pNode->GetFont().GetColor() != COL_AUTO;
return drawingml::DOCUMENT_DOCX == m_DocumentType;
}
// Only a bold or italic command around a construct sets these attributes on it.
bool SmOoxmlExport::HasCtrlPr( const SmNode* pNode ) const
// Only a color command around a node sets a color on it.
bool SmOoxmlExport::HasOwnColor( const SmNode* pNode )
{
return HasOwnColor( pNode )
|| ( drawingml::DOCUMENT_DOCX == m_DocumentType
&& ( pNode->Attributes() & ( FontAttribute::Bold | FontAttribute::Italic )));
return pNode->GetFont().GetColor() != COL_AUTO;
}
// The size belongs on every run and on every control property of the formula, which is
// where the markup carries it.
void SmOoxmlExport::WriteFontSize()
{
const OString sFontSize( OString::number( m_nFontSizeInHalfPoints ));
m_pSerializer->singleElementNS( XML_w, XML_sz, FSNS( XML_w, XML_val ), sFontSize );
m_pSerializer->singleElementNS( XML_w, XML_szCs, FSNS( XML_w, XML_val ), sFontSize );
}
// A construct such as a fraction or a pair of brackets draws part of itself, and the
// format of that part travels in the m:ctrlPr of the construct's property element.
void SmOoxmlExport::WriteCtrlPr( const SmNode* pNode )
{
if( !HasCtrlPr( pNode ))
if( !WritesRunProperties())
return;
m_pSerializer->startElementNS( XML_m, XML_ctrlPr );
m_pSerializer->startElementNS( XML_w, XML_rPr );
@@ -115,10 +122,21 @@ void SmOoxmlExport::WriteCtrlPr( const SmNode* pNode )
if( HasOwnColor( pNode ))
m_pSerializer->singleElementNS( XML_w, XML_color, FSNS( XML_w, XML_val ),
msfilter::util::ConvertColorOU( pNode->GetFont().GetColor()));
WriteFontSize();
m_pSerializer->endElementNS( XML_w, XML_rPr );
m_pSerializer->endElementNS( XML_m, XML_ctrlPr );
}
// A property element that a construct carries only to hold its control properties.
void SmOoxmlExport::WritePropertiesElement( sal_Int32 nElement, const SmNode* pNode )
{
if( !WritesRunProperties())
return;
m_pSerializer->startElementNS( XML_m, nElement );
WriteCtrlPr( pNode );
m_pSerializer->endElementNS( XML_m, nElement );
}
static bool lcl_HasLetter( const OUString& rText )
{
for( sal_Int32 nIndex = 0; nIndex < rText.getLength(); )
@@ -145,6 +163,7 @@ static const char* lcl_GetOoxmlStyle( bool bBold, bool bItalic, const OUString&
void SmOoxmlExport::HandleVerticalStack( const SmNode* pNode, int nLevel )
{
m_pSerializer->startElementNS(XML_m, XML_eqArr);
WritePropertiesElement( XML_eqArrPr, pNode );
int size = pNode->GetNumSubNodes();
for( int i = 0;
i < size;
@@ -181,32 +200,24 @@ void SmOoxmlExport::HandleText( const SmNode* pNode, int /*nLevel*/)
m_pSerializer->singleElementNS(XML_m, XML_sty, FSNS(XML_m, XML_val), pStyle);
m_pSerializer->endElementNS( XML_m, XML_rPr );
}
const bool bWriteFont
= drawingml::DOCUMENT_DOCX == m_DocumentType && ECMA_376_1ST_EDITION == version;
const bool bWriteBold
= drawingml::DOCUMENT_DOCX == m_DocumentType && bLiteral && bBold;
const bool bWriteItalic
= drawingml::DOCUMENT_DOCX == m_DocumentType && bLiteral && bItalic;
// A surrounding color, bold or italic command has already reached this node.
const Color aColor = pNode->GetFont().GetColor();
const bool bWriteColor
= drawingml::DOCUMENT_DOCX == m_DocumentType && aColor != COL_AUTO;
if( bWriteFont || bWriteBold || bWriteItalic || bWriteColor )
if( WritesRunProperties())
{
m_pSerializer->startElementNS(XML_w, XML_rPr);
if( bWriteFont )
// HACK: MSOffice2007 does not import characters properly unless this font is
// explicitly given
if( ECMA_376_1ST_EDITION == version )
// Readers of the first OOXML edition get the characters wrong unless the font
// is named here.
m_pSerializer->singleElementNS( XML_w, XML_rFonts,
FSNS( XML_w, XML_ascii ), "Cambria Math",
FSNS( XML_w, XML_hAnsi ), "Cambria Math" );
if( bWriteBold )
// A surrounding color, bold or italic command has already reached this node.
if( bLiteral && bBold )
m_pSerializer->singleElementNS( XML_w, XML_b );
if( bWriteItalic )
if( bLiteral && bItalic )
m_pSerializer->singleElementNS( XML_w, XML_i );
if( bWriteColor )
if( HasOwnColor( pNode ))
m_pSerializer->singleElementNS( XML_w, XML_color, FSNS( XML_w, XML_val ),
msfilter::util::ConvertColorOU( aColor ));
msfilter::util::ConvertColorOU( pNode->GetFont().GetColor()));
WriteFontSize();
m_pSerializer->endElementNS( XML_w, XML_rPr );
}
m_pSerializer->startElementNS(XML_m, XML_t, FSNS(XML_xml, XML_space), "preserve");
@@ -270,7 +281,7 @@ void SmOoxmlExport::HandleText( const SmNode* pNode, int /*nLevel*/)
void SmOoxmlExport::HandleFractions( const SmNode* pNode, int nLevel, const char* type )
{
m_pSerializer->startElementNS(XML_m, XML_f);
if( type != nullptr || HasCtrlPr( pNode ))
if( type != nullptr || WritesRunProperties())
{
m_pSerializer->startElementNS(XML_m, XML_fPr);
if( type != nullptr )
@@ -359,27 +370,24 @@ void SmOoxmlExport::HandleAttribute( const SmAttributeNode* pNode, int nLevel )
void SmOoxmlExport::HandleRoot( const SmRootNode* pNode, int nLevel )
{
const SmNode* pArgument = pNode->Argument();
m_pSerializer->startElementNS(XML_m, XML_rad);
if( const SmNode* argument = pNode->Argument())
{
if( HasCtrlPr( pNode ))
{
m_pSerializer->startElementNS(XML_m, XML_radPr);
WriteCtrlPr( pNode );
m_pSerializer->endElementNS( XML_m, XML_radPr );
}
m_pSerializer->startElementNS(XML_m, XML_deg);
HandleNode( argument, nLevel + 1 );
m_pSerializer->endElementNS( XML_m, XML_deg );
}
else
if( pArgument == nullptr || WritesRunProperties())
{
m_pSerializer->startElementNS(XML_m, XML_radPr);
if( pArgument == nullptr )
m_pSerializer->singleElementNS(XML_m, XML_degHide, FSNS(XML_m, XML_val), "1");
WriteCtrlPr( pNode );
m_pSerializer->endElementNS( XML_m, XML_radPr );
m_pSerializer->singleElementNS(XML_m, XML_deg); // empty but present
}
if( pArgument != nullptr )
{
m_pSerializer->startElementNS(XML_m, XML_deg);
HandleNode( pArgument, nLevel + 1 );
m_pSerializer->endElementNS( XML_m, XML_deg );
}
else
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 );
@@ -448,8 +456,10 @@ void SmOoxmlExport::HandleOperator( const SmOperNode* pNode, int nLevel )
}
case TLIM:
m_pSerializer->startElementNS(XML_m, XML_func);
WritePropertiesElement( XML_funcPr, pNode );
m_pSerializer->startElementNS(XML_m, XML_fName);
m_pSerializer->startElementNS(XML_m, XML_limLow);
WritePropertiesElement( XML_limLowPr, pNode );
m_pSerializer->startElementNS(XML_m, XML_e);
HandleNode( pNode->GetSymbol(), nLevel + 1 );
m_pSerializer->endElementNS( XML_m, XML_e );
@@ -484,6 +494,7 @@ void SmOoxmlExport::HandleSubSupScriptInternal( const SmSubSupNode* pNode, int n
if(( flags & ( 1 << RSUP | 1 << RSUB )) == ( 1 << RSUP | 1 << RSUB ))
{ // m:sSubSup
m_pSerializer->startElementNS(XML_m, XML_sSubSup);
WritePropertiesElement( XML_sSubSupPr, pNode );
m_pSerializer->startElementNS(XML_m, XML_e);
flags &= ~( 1 << RSUP | 1 << RSUB );
if( flags == 0 )
@@ -502,6 +513,7 @@ void SmOoxmlExport::HandleSubSupScriptInternal( const SmSubSupNode* pNode, int n
else if(( flags & ( 1 << RSUB )) == 1 << RSUB )
{ // m:sSub
m_pSerializer->startElementNS(XML_m, XML_sSub);
WritePropertiesElement( XML_sSubPr, pNode );
m_pSerializer->startElementNS(XML_m, XML_e);
flags &= ~( 1 << RSUB );
if( flags == 0 )
@@ -517,6 +529,7 @@ void SmOoxmlExport::HandleSubSupScriptInternal( const SmSubSupNode* pNode, int n
else if(( flags & ( 1 << RSUP )) == 1 << RSUP )
{ // m:sSup
m_pSerializer->startElementNS(XML_m, XML_sSup);
WritePropertiesElement( XML_sSupPr, pNode );
m_pSerializer->startElementNS(XML_m, XML_e);
flags &= ~( 1 << RSUP );
if( flags == 0 )
@@ -532,6 +545,7 @@ void SmOoxmlExport::HandleSubSupScriptInternal( const SmSubSupNode* pNode, int n
else if(( flags & ( 1 << LSUP | 1 << LSUB )) == ( 1 << LSUP | 1 << LSUB ))
{ // m:sPre
m_pSerializer->startElementNS(XML_m, XML_sPre);
WritePropertiesElement( XML_sPrePr, pNode );
m_pSerializer->startElementNS(XML_m, XML_sub);
HandleNode( pNode->GetSubSup( LSUB ), nLevel + 1 );
m_pSerializer->endElementNS( XML_m, XML_sub );
@@ -550,6 +564,7 @@ void SmOoxmlExport::HandleSubSupScriptInternal( const SmSubSupNode* pNode, int n
else if(( flags & ( 1 << CSUB )) == ( 1 << CSUB ))
{ // m:limLow looks like a good element for central superscript
m_pSerializer->startElementNS(XML_m, XML_limLow);
WritePropertiesElement( XML_limLowPr, pNode );
m_pSerializer->startElementNS(XML_m, XML_e);
flags &= ~( 1 << CSUB );
if( flags == 0 )
@@ -565,6 +580,7 @@ void SmOoxmlExport::HandleSubSupScriptInternal( const SmSubSupNode* pNode, int n
else if(( flags & ( 1 << CSUP )) == ( 1 << CSUP ))
{ // m:limUpp looks like a good element for central superscript
m_pSerializer->startElementNS(XML_m, XML_limUpp);
WritePropertiesElement( XML_limUppPr, pNode );
m_pSerializer->startElementNS(XML_m, XML_e);
flags &= ~( 1 << CSUP );
if( flags == 0 )
@@ -588,6 +604,7 @@ void SmOoxmlExport::HandleSubSupScriptInternal( const SmSubSupNode* pNode, int n
void SmOoxmlExport::HandleMatrix( const SmMatrixNode* pNode, int nLevel )
{
m_pSerializer->startElementNS(XML_m, XML_m);
WritePropertiesElement( XML_mPr, pNode );
for (size_t row = 0; row < pNode->GetNumRows(); ++row)
{
m_pSerializer->startElementNS(XML_m, XML_mr);
@@ -675,6 +692,7 @@ void SmOoxmlExport::HandleVerticalBrace( const SmVerticalBraceNode* pNode, int n
{
bool top = ( pNode->GetToken().eType == TOVERBRACE );
m_pSerializer->startElementNS(XML_m, top ? XML_limUpp : XML_limLow);
WritePropertiesElement( top ? XML_limUppPr : XML_limLowPr, pNode );
m_pSerializer->startElementNS(XML_m, XML_e);
m_pSerializer->startElementNS(XML_m, XML_groupChr);
m_pSerializer->startElementNS(XML_m, XML_groupChrPr);
@@ -708,6 +726,13 @@ void SmOoxmlExport::HandleVerticalBrace( const SmVerticalBraceNode* pNode, int n
void SmOoxmlExport::HandleBlank()
{
m_pSerializer->startElementNS(XML_m, XML_r);
// A blank draws no glyph, so the size is all it carries.
if( WritesRunProperties())
{
m_pSerializer->startElementNS( XML_w, XML_rPr );
WriteFontSize();
m_pSerializer->endElementNS( XML_w, XML_rPr );
}
m_pSerializer->startElementNS(XML_m, XML_t, FSNS(XML_xml, XML_space), "preserve");
m_pSerializer->write( " " );
m_pSerializer->endElementNS( XML_m, XML_t );
+13 -8
View File
@@ -22,7 +22,7 @@ class SmOoxmlExport : public SmWordExportBase
{
public:
SmOoxmlExport(const SmNode* pIn, oox::core::OoxmlVersion version,
oox::drawingml::DocumentType documentType);
oox::drawingml::DocumentType documentType, sal_Int32 nFontSizeInHalfPoints);
void ConvertFromStarMath(const ::sax_fastparser::FSHelperPtr& m_pSerializer, const sal_Int8);
private:
@@ -37,18 +37,23 @@ private:
void HandleBrace(const SmBraceNode* pNode, int nLevel) override;
void HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel) override;
void HandleBlank() override;
/// Writes the m:ctrlPr carrying the color, bold and italic of the parts a construct
/// draws itself.
/// True when the run properties are written out, which needs a DOCX.
bool WritesRunProperties() const;
/// True when the node has a color of its own rather than the default one.
static bool HasOwnColor(const SmNode* pNode);
/// Writes the w:sz and w:szCs carrying the size the formula is drawn at.
void WriteFontSize();
/// Writes the m:ctrlPr carrying the size, color, bold and italic of the parts a
/// construct draws itself.
void WriteCtrlPr(const SmNode* pNode);
/// True when the node has a color, bold or italic of its own to write in an m:ctrlPr.
bool HasCtrlPr(const SmNode* pNode) const;
/// True when the color of the node is written out, which needs a color other than
/// the default and a DOCX.
bool HasOwnColor(const SmNode* pNode) const;
/// Writes a property element that a construct carries only to hold its control properties.
void WritePropertiesElement(sal_Int32 nElement, const SmNode* pNode);
::sax_fastparser::FSHelperPtr m_pSerializer;
oox::core::OoxmlVersion version;
/// needed to determine markup for nested run properties
oox::drawingml::DocumentType const m_DocumentType;
/// The size the formula is drawn at, in half points.
sal_Int32 const m_nFontSizeInHalfPoints;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+17
View File
@@ -9,6 +9,7 @@
#include <sal/config.h>
#include <algorithm>
#include <string_view>
#include "ooxmlimport.hxx"
@@ -201,6 +202,22 @@ OUString SmOoxmlImport::ConvertToStarMath()
return handleStream();
}
sal_Int32 SmOoxmlImport::GetFontSizeInHalfPoints() const
{
// A formula is drawn at a single size, and the runs of a formula that names a size all
// carry it. Where they differ, the largest one is the size of the body: a smaller one
// belongs to a part the author shrank, such as a limit or a script. A value that is not
// a positive number of half points counts as no size at all, and so does a formula whose
// runs name none, which then takes the size of the text around it.
sal_Int32 nFontSize = 0;
for( const OUString& rValue : m_rStream.tagAttributesWithin(
M_TOKEN( r ), W_TOKEN( sz ), W_TOKEN( val )))
{
nFontSize = std::max( nFontSize, rValue.toInt32());
}
return nFontSize;
}
// "toplevel" of reading, there will be oMath (if there was oMathPara, that was
// up to the parent component to handle)
+4
View File
@@ -10,6 +10,7 @@
#pragma once
#include <rtl/ustring.hxx>
#include <sal/types.h>
#include <memory>
@@ -25,6 +26,9 @@ public:
explicit SmOoxmlImport( oox::formulaimport::XmlStream& stream );
~SmOoxmlImport();
OUString ConvertToStarMath();
/** The font size the markup gives for the formula, in half points, or 0 when the markup
carries no size of its own and the formula takes the size of the text around it. */
sal_Int32 GetFontSizeInHalfPoints() const;
private:
/// The parser that tells whether a run's text stands as an expression of its own.
SmParser5& getParser();
+15 -5
View File
@@ -480,12 +480,12 @@ void SmModel::_setPropertyValues(const PropertyMapEntry** ppEntries, const Any*
throw IllegalArgumentException();
Size aSize = aFormat.GetBaseSize();
aSize.setHeight(o3tl::convert(nVal, o3tl::Length::pt, SmO3tlLengthUnit()));
aFormat.SetBaseSize(aSize);
aFormat.SetBaseSizeAndFonts(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);
// A font that this same call names is merged in below, from a copy that
// needs the new size too.
for (sal_uInt16 nFont = FNT_BEGIN; nFont <= FNT_END; nFont++)
maFonts[nFont].SetSize(aSize);
}
break;
case HANDLE_RELATIVE_FONT_HEIGHT_TEXT :
@@ -1112,6 +1112,16 @@ void SmModel::readFormulaOoxml( oox::formulaimport::XmlStream& stream )
GetSmDocShell()->readFormulaOoxml(stream);
}
sal_Int32 SmModel::getFormulaFontSizeInHalfPoints() const
{
return GetSmDocShell()->GetOoxmlFontSizeInHalfPoints();
}
void SmModel::setFormulaFontSizeInHalfPoints( sal_Int32 nFontSize )
{
GetSmDocShell()->SetFontSizeInHalfPoints(nFontSize);
}
Size SmModel::getFormulaSize() const
{
return o3tl::convert(GetSmDocShell()->GetSize(), SmO3tlLengthUnit(), o3tl::Length::mm100);
Binary file not shown.
+8 -1
View File
@@ -434,7 +434,14 @@ CPPUNIT_TEST_FIXTURE(Test, testMathBoldItalic)
assertXPath(pXmlDoc, "//w:p[1]//m:f/m:num/m:r/m:rPr/m:sty", "val", u"bi");
assertXPath(pXmlDoc, "//w:p[1]//m:f/m:den/m:r/m:rPr", 0);
assertXPath(pXmlDoc, "//w:p[1]//m:f/m:fPr", 0);
// The fraction's control properties carry the size it is drawn at and nothing else: a
// bold or italic there belongs to the bar alone, and a command around the fraction would
// reach the terms as well. The size comes first, so the three counts below are taken on
// a path that is there.
assertXPath(pXmlDoc, "//w:p[1]//m:f/m:fPr/m:ctrlPr/w:rPr/w:sz", 1);
assertXPath(pXmlDoc, "//w:p[1]//m:f/m:fPr/m:ctrlPr/w:rPr/w:b", 0);
assertXPath(pXmlDoc, "//w:p[1]//m:f/m:fPr/m:ctrlPr/w:rPr/w:i", 0);
assertXPath(pXmlDoc, "//w:p[1]//m:f/m:fPr/m:ctrlPr/w:rPr/w:color", 0);
assertXPath(pXmlDoc, "//w:p[2]//m:oMath/m:r[1]/w:rPr/w:b", 0);
assertXPath(pXmlDoc, "(//w:p[3]//m:sSub)[1]/m:sub/m:r/m:rPr/m:sty", "val", u"b");
assertXPath(pXmlDoc, "(//w:p[3]//m:f)[1]/m:num/m:r/m:rPr/m:sty", "val", u"bi");
@@ -12,6 +12,7 @@
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/document/XEmbeddedObjectSupplier2.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/style/DropCapFormat.hpp>
#include <com/sun/star/text/HoriOrientation.hpp>
@@ -419,6 +420,37 @@ CPPUNIT_TEST_FIXTURE(Test, testTableAlignmentFromTableStyle)
verify();
}
CPPUNIT_TEST_FIXTURE(Test, testEquationFontSize)
{
// Each equation is drawn at the size its run properties give it, and an equation whose runs
// give none takes the 11pt of the text around it. Without the fix all three came out at the
// 12pt default of the formula editor.
createSwDoc("math-font-size.docx");
const sal_Int16 aExpectedSizes[] = { 8, 11, 20 };
for (size_t i = 0; i < std::size(aExpectedSizes); ++i)
{
uno::Reference<document::XEmbeddedObjectSupplier2> xFormula(getShape(i + 1),
uno::UNO_QUERY);
CPPUNIT_ASSERT(xFormula.is());
CPPUNIT_ASSERT_EQUAL(
aExpectedSizes[i],
getProperty<sal_Int16>(xFormula->getEmbeddedObject(), u"BaseFontHeight"_ustr));
}
save(TestFilter::DOCX);
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
// The size goes back out on the equation's runs, in half points, so the sizes survive a
// round trip. Without the fix the export wrote no size at all.
assertXPath(pXmlDoc, "//w:p[1]/m:oMath/m:r/w:rPr/w:sz", "val", u"16");
assertXPath(pXmlDoc, "//w:p[2]/m:oMath/m:r/w:rPr/w:sz", "val", u"22");
assertXPath(pXmlDoc, "//w:p[3]/m:oMath/m:f/m:num/m:r/w:rPr/w:sz", "val", u"40");
// The fraction bar is drawn by the fraction itself, and takes its size from the control
// properties rather than from the runs above and below it.
assertXPath(pXmlDoc, "//w:p[3]/m:oMath/m:f/m:fPr/m:ctrlPr/w:rPr/w:sz", "val", u"40");
}
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
@@ -20,13 +20,13 @@ class tdf157569(UITestCase):
nHeight = xShape.getSize().Height
nWidth = xShape.getSize().Width
# Without the fix in place, this test would have failed with
# AssertionError: 1663 != 944
self.assertEqual(1663, nHeight)
# The formula takes the 11pt of the text around it, which the document defaults
# hold, rather than the 12pt default of the formula editor.
self.assertEqual(1529, nHeight)
if platform.system() == "Windows":
self.assertEqual(2157, nWidth) # no idea why it's different on Windows
self.assertEqual(1976, nWidth) # no idea why it's different on Windows
else:
self.assertEqual(2118, nWidth)
self.assertEqual(1940, nWidth)
xDoc = self.xUITest.getTopFocusWindow()
xEditWin = xDoc.getChild("writer_edit")
@@ -3844,9 +3844,21 @@ void DomainMapper_Impl::appendStarMath( const Value& val )
uno::Any(sal_Int32(0)));
xComponentProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ),
uno::Any(sal_Int32(0)));
auto* pFormula = dynamic_cast<oox::FormulaImExportBase*>(xInterface.get());
// A formula whose own markup carries no font size is drawn at the size of the text
// around it, which the styles and the document defaults hold.
double fCharHeight = 0;
if (pFormula && pFormula->getFormulaFontSizeInHalfPoints() == 0
&& (GetAnyProperty(PROP_CHAR_HEIGHT, GetTopContextOfType(CONTEXT_CHARACTER))
>>= fCharHeight))
{
pFormula->setFormulaFontSizeInHalfPoints(std::lround(fCharHeight * 2));
}
Size size( 1000, 1000 );
if( oox::FormulaImExportBase* formulaimport = dynamic_cast< oox::FormulaImExportBase* >( xInterface.get()))
size = formulaimport->getFormulaSize();
if( pFormula )
size = pFormula->getFormulaSize();
xStarMath->setPropertyValue(getPropertyName( PROP_WIDTH ),
uno::Any( sal_Int32(size.Width())));
xStarMath->setPropertyValue(getPropertyName( PROP_HEIGHT ),