[Libreoffice-commits] core.git: Branch 'private/swe/libreoffice-5-2+backports' - 2 commits - include/oox oox/source sw/qa
Samuel Mehrbrodt
Samuel.Mehrbrodt at cib.de
Fri Dec 8 10:11:35 UTC 2017
include/oox/export/vmlexport.hxx | 15 +
oox/source/export/vmlexport.cxx | 91 +++++++++-
oox/source/vml/vmlshape.cxx | 69 ++++---
sw/qa/extras/ooxmlexport/data/signature-line-all-props-set.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport6.cxx | 4
sw/qa/extras/ooxmlexport/ooxmlexport7.cxx | 40 ++++
6 files changed, 182 insertions(+), 37 deletions(-)
New commits:
commit 662b85c22da5d3c2c018022098828743633beedc
Author: Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>
Date: Fri Dec 8 10:56:20 2017 +0100
Signature lines need to be wrapped in v:shape, not v:rect
Partial backport of c0cc02e2934aeb12dda44818955e5964496c186a
Change-Id: I4532b22c16ad76fa290c142fa7029cc3ef203388
diff --git a/include/oox/export/vmlexport.hxx b/include/oox/export/vmlexport.hxx
index 37b2af0b8305..433e3b6d7413 100644
--- a/include/oox/export/vmlexport.hxx
+++ b/include/oox/export/vmlexport.hxx
@@ -82,6 +82,7 @@ class OOX_DLLPUBLIC VMLExport : public EscherEx
/// Anchoring.
sal_Int16 m_eHOri, m_eVOri, m_eHRel, m_eVRel;
+ bool m_bInline; // css::text::TextContentAnchorType_AS_CHARACTER
/// Parent position.
const Point* m_pNdTopLeft;
@@ -101,9 +102,18 @@ class OOX_DLLPUBLIC VMLExport : public EscherEx
/// Remember style, the most important shape attribute ;-)
OStringBuffer *m_pShapeStyle;
+ /// Remember the generated shape id.
+ OString m_sShapeId;
+
/// Remember which shape types we had already written.
bool *m_pShapeTypeWritten;
+ /// It seems useless to write out an XML_ID attribute next to XML_id which defines the actual shape id
+ bool m_bSkipwzName;
+
+ /// Use '#' mark for type attribute (check Type Attribute of VML shape in OOXML documentation)
+ bool m_bUseHashMarkForType;
+
public:
VMLExport( ::sax_fastparser::FSHelperPtr pSerializer, VMLTextExport* pTextExport = nullptr );
virtual ~VMLExport();
@@ -116,11 +126,14 @@ public:
/// Export the sdr object as VML.
///
/// Call this when you need to export the object as VML.
- void AddSdrObject( const SdrObject& rObj, sal_Int16 eHOri = -1,
+ OString AddSdrObject( const SdrObject& rObj, sal_Int16 eHOri = -1,
sal_Int16 eVOri = -1, sal_Int16 eHRel = -1,
sal_Int16 eVRel = -1, const Point* pNdTopLeft = nullptr, const bool bOOxmlExport = false );
+ OString AddInlineSdrObject( const SdrObject& rObj, const bool bOOxmlExport = false );
virtual void AddSdrObjectVMLObject( const SdrObject& rObj) override;
static bool IsWaterMarkShape(const OUString& rStr);
+ void SetSkipwzName() { m_bSkipwzName = true; }
+ void SetHashMarkForType() { m_bUseHashMarkForType = true; }
protected:
/// Add an attribute to the generated <v:shape/> element.
///
diff --git a/oox/source/export/vmlexport.cxx b/oox/source/export/vmlexport.cxx
index ca2b95a3e919..241f742a7f63 100644
--- a/oox/source/export/vmlexport.cxx
+++ b/oox/source/export/vmlexport.cxx
@@ -58,6 +58,7 @@ VMLExport::VMLExport( ::sax_fastparser::FSHelperPtr pSerializer, VMLTextExport*
, m_eVOri( 0 )
, m_eHRel( 0 )
, m_eVRel( 0 )
+ , m_bInline( false )
, m_pNdTopLeft( nullptr )
, m_pSdrObject( nullptr )
, m_pShapeAttrList( nullptr )
@@ -65,6 +66,8 @@ VMLExport::VMLExport( ::sax_fastparser::FSHelperPtr pSerializer, VMLTextExport*
, m_nShapeFlags(0)
, m_pShapeStyle( new OStringBuffer( 200 ) )
, m_pShapeTypeWritten( new bool[ ESCHER_ShpInst_COUNT ] )
+ , m_bSkipwzName( false )
+ , m_bUseHashMarkForType( false )
{
mnGroupLevel = 1;
memset( m_pShapeTypeWritten, 0, ESCHER_ShpInst_COUNT * sizeof( bool ) );
@@ -187,17 +190,20 @@ void VMLExport::AddShape( sal_uInt32 nShapeType, sal_uInt32 nShapeFlags, sal_uIn
{
m_nShapeType = nShapeType;
m_nShapeFlags = nShapeFlags;
+
+ m_sShapeId = ShapeIdString( nShapeId );
// If shape is a watermark object - should keep the original shape's name
// because Microsoft detects if it is a watermark by the actual name
if (!IsWaterMarkShape(m_pSdrObject->GetName()))
{
// Not a watermark object
- m_pShapeAttrList->add( XML_id, ShapeIdString( nShapeId ) );
+ m_pShapeAttrList->add( XML_id, m_sShapeId );
}
else
{
// A watermark object - store the optional shape ID also ('o:spid')
m_pShapeAttrList->add( XML_id, OUStringToOString(m_pSdrObject->GetName(), RTL_TEXTENCODING_UTF8) );
+ m_pShapeAttrList->addNS( XML_o, XML_spid, m_sShapeId );
}
}
@@ -924,7 +930,7 @@ void VMLExport::Commit( EscherPropertyContainer& rProps, const Rectangle& rRect
aStream.Seek(0);
OUString idStr = SvxMSDffManager::MSDFFReadZString(aStream, it->nPropSize, true);
aStream.Seek(0);
- if (!IsWaterMarkShape(m_pSdrObject->GetName()))
+ if (!IsWaterMarkShape(m_pSdrObject->GetName()) && !m_bSkipwzName)
m_pShapeAttrList->add(XML_ID, OUStringToOString(idStr, RTL_TEXTENCODING_UTF8).getStr());
bAlreadyWritten[ESCHER_Prop_wzName] = true;
@@ -1014,13 +1020,18 @@ void VMLExport::AddRectangleDimensions( OStringBuffer& rBuffer, const Rectangle&
if ( !rBuffer.isEmpty() )
rBuffer.append( ";" );
- if (rbAbsolutePos)
+ if (rbAbsolutePos && !m_bInline)
{
rBuffer.append( "position:absolute;" );
}
- if ( mnGroupLevel == 1 )
+ if(m_bInline)
{
+ rBuffer.append( "width:" ).append( double( rRectangle.Right() - rRectangle.Left() ) / 20 )
+ .append( "pt;height:" ).append( double( rRectangle.Bottom() - rRectangle.Top() ) / 20 )
+ .append( "pt" );
+ }
+ else if ( mnGroupLevel == 1 ) {
rBuffer.append( "margin-left:" ).append( double( rRectangle.Left() ) / 20 )
.append( "pt;margin-top:" ).append( double( rRectangle.Top() ) / 20 )
.append( "pt;width:" ).append( double( rRectangle.Right() - rRectangle.Left() ) / 20 )
@@ -1106,6 +1117,62 @@ sal_Int32 VMLExport::StartShape()
case ESCHER_ShpInst_Ellipse: nShapeElement = XML_oval; break;
case ESCHER_ShpInst_Arc: nShapeElement = XML_arc; break;
case ESCHER_ShpInst_Line: nShapeElement = XML_line; break;
+ case ESCHER_ShpInst_HostControl:
+ {
+ // We don't have a shape definition for host control in presetShapeDefinitions.xml
+ // So use a definition copied from DOCX file created with MSO
+ bReferToShapeType = true;
+ nShapeElement = XML_shape;
+ if ( !m_pShapeTypeWritten[ m_nShapeType ] )
+ {
+ OStringBuffer sShapeType;
+ sShapeType.append("<v:shapetype id=\"shapetype_").append(OString::number(m_nShapeType)).
+ append("\" coordsize=\"21600,21600\" o:spt=\"").append(OString::number(m_nShapeType)).
+ append("\" path=\"m,l,21600l21600,21600l21600,xe\">\n").
+ append("<v:stroke joinstyle=\"miter\"/>\n"
+ "<v:path shadowok=\"f\" o:extrusionok=\"f\" strokeok=\"f\" fillok=\"f\" o:connecttype=\"rect\"/>\n"
+ "<o:lock v:ext=\"edit\" shapetype=\"t\"/>\n"
+ "</v:shapetype>");
+ m_pSerializer->write(sShapeType.makeStringAndClear().getStr());
+ m_pShapeTypeWritten[ m_nShapeType ] = true;
+ }
+ break;
+ }
+ case ESCHER_ShpInst_PictureFrame:
+ {
+ // We don't have a shape definition for picture frame in presetShapeDefinitions.xml
+ // So use a definition copied from DOCX file created with MSO
+ bReferToShapeType = true;
+ nShapeElement = XML_shape;
+ if ( !m_pShapeTypeWritten[ m_nShapeType ] )
+ {
+ OStringBuffer sShapeType;
+ sShapeType.append("<v:shapetype id=\"shapetype_").append(OString::number(m_nShapeType)).
+ append("\" coordsize=\"21600,21600\" o:spt=\"").append(OString::number(m_nShapeType)).
+ append("\" o:preferrelative=\"t\" path=\"m at 4@5l at 4@11 at 9@11 at 9@5xe\" filled=\"f\" stroked=\"f\">\n").
+ append("<v:stroke joinstyle=\"miter\"/>\n"
+ "<v:formulas>\n"
+ "<v:f eqn=\"if lineDrawn pixelLineWidth 0\"/>\n"
+ "<v:f eqn=\"sum @0 1 0\"/>\n"
+ "<v:f eqn=\"sum 0 0 @1\"/>\n"
+ "<v:f eqn=\"prod @2 1 2\"/>\n"
+ "<v:f eqn=\"prod @3 21600 pixelWidth\"/>\n"
+ "<v:f eqn=\"prod @3 21600 pixelHeight\"/>\n"
+ "<v:f eqn=\"sum @0 0 1\"/>\n"
+ "<v:f eqn=\"prod @6 1 2\"/>\n"
+ "<v:f eqn=\"prod @7 21600 pixelWidth\"/>\n"
+ "<v:f eqn=\"sum @8 21600 0\"/>\n"
+ "<v:f eqn=\"prod @7 21600 pixelHeight\"/>\n"
+ "<v:f eqn=\"sum @10 21600 0\"/>\n"
+ "</v:formulas>\n"
+ "<v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\"/>\n"
+ "<o:lock v:ext=\"edit\" aspectratio=\"t\"/>\n"
+ "</v:shapetype>");
+ m_pSerializer->write(sShapeType.makeStringAndClear().getStr());
+ m_pShapeTypeWritten[ m_nShapeType ] = true;
+ }
+ break;
+ }
default:
if ( m_nShapeType < ESCHER_ShpInst_COUNT )
{
@@ -1212,7 +1279,10 @@ sal_Int32 VMLExport::StartShape()
if ( nShapeElement >= 0 && !m_pShapeAttrList->hasAttribute( XML_type ) && bReferToShapeType )
{
- m_pShapeAttrList->add( XML_type, OStringBuffer( 20 )
+ OStringBuffer sTypeBuffer( 20 );
+ if (m_bUseHashMarkForType)
+ sTypeBuffer.append("#");
+ m_pShapeAttrList->add( XML_type, sTypeBuffer
.append( "shapetype_" ).append( sal_Int32( m_nShapeType ) )
.makeStringAndClear() );
}
@@ -1285,7 +1355,7 @@ void VMLExport::EndShape( sal_Int32 nShapeElement )
}
}
-void VMLExport::AddSdrObject( const SdrObject& rObj, sal_Int16 eHOri, sal_Int16 eVOri, sal_Int16 eHRel, sal_Int16 eVRel, const Point* pNdTopLeft, const bool bOOxmlExport )
+OString VMLExport::AddSdrObject( const SdrObject& rObj, sal_Int16 eHOri, sal_Int16 eVOri, sal_Int16 eHRel, sal_Int16 eVRel, const Point* pNdTopLeft, const bool bOOxmlExport )
{
m_pSdrObject = &rObj;
m_eHOri = eHOri;
@@ -1294,6 +1364,15 @@ void VMLExport::AddSdrObject( const SdrObject& rObj, sal_Int16 eHOri, sal_Int16
m_eVRel = eVRel;
m_pNdTopLeft = pNdTopLeft;
EscherEx::AddSdrObject(rObj, bOOxmlExport);
+ return m_sShapeId;
+}
+
+OString VMLExport::AddInlineSdrObject( const SdrObject& rObj, const bool bOOxmlExport )
+{
+ m_pSdrObject = &rObj;
+ m_bInline = true;
+ EscherEx::AddSdrObject(rObj, bOOxmlExport);
+ return m_sShapeId;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
index df6f945e747a..85d4138e4578 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
@@ -455,7 +455,7 @@ DECLARE_OOXMLEXPORT_TEST(testVMLData, "TestVMLData.docx")
xmlDocPtr pXmlDoc = parseExport("word/header2.xml");
if (!pXmlDoc)
return;
- CPPUNIT_ASSERT(getXPath(pXmlDoc, "/w:hdr/w:p/w:r/mc:AlternateContent/mc:Fallback/w:pict/v:rect", "stroked").match("f"));
+ CPPUNIT_ASSERT(getXPath(pXmlDoc, "/w:hdr/w:p/w:r/mc:AlternateContent/mc:Fallback/w:pict/v:shape", "stroked").match("f"));
}
DECLARE_OOXMLEXPORT_TEST(testImageData, "image_data.docx")
@@ -465,7 +465,7 @@ DECLARE_OOXMLEXPORT_TEST(testImageData, "image_data.docx")
xmlDocPtr pXmlDoc = parseExport("word/header2.xml");
if (!pXmlDoc)
return;
- CPPUNIT_ASSERT(getXPath(pXmlDoc, "/w:hdr/w:p/w:r/mc:AlternateContent/mc:Fallback/w:pict/v:rect/v:imagedata", "detectmouseclick").match("t"));
+ CPPUNIT_ASSERT(getXPath(pXmlDoc, "/w:hdr/w:p/w:r/mc:AlternateContent/mc:Fallback/w:pict/v:shape/v:imagedata", "detectmouseclick").match("t"));
}
DECLARE_OOXMLEXPORT_TEST(testFdo70838, "fdo70838.docx")
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
index 4d6014dede64..a0335f5f29b1 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
@@ -141,7 +141,7 @@ DECLARE_OOXMLEXPORT_TEST(testPictureWatermark, "pictureWatermark.docx")
return;
// Check the watermark ID
- assertXPath(pXmlHeader1, "/w:hdr[1]/w:p[1]/w:r[1]/mc:AlternateContent[1]/mc:Fallback[1]/w:pict[1]/v:rect[1]","id","WordPictureWatermark11962361");
+ assertXPath(pXmlHeader1, "/w:hdr[1]/w:p[1]/w:r[1]/mc:AlternateContent[1]/mc:Fallback[1]/w:pict[1]/v:shape[1]","id","WordPictureWatermark11962361");
}
commit db319dc5039f45288fc0ad6abc1a903cb9596ed3
Author: Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>
Date: Thu Nov 23 15:05:03 2017 +0100
tdf#83877 Unit test for OOXML SignatureLine Roundtrip
Change-Id: I40c116f28c0e8efe81e33e48fa05098d1dd76731
Reviewed-on: https://gerrit.libreoffice.org/45152
Reviewed-by: Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>
Tested-by: Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>
diff --git a/oox/source/vml/vmlshape.cxx b/oox/source/vml/vmlshape.cxx
index 72c77c7dddee..32fe1cb8751d 100644
--- a/oox/source/vml/vmlshape.cxx
+++ b/oox/source/vml/vmlshape.cxx
@@ -1183,40 +1183,55 @@ Reference< XShape > ComplexShape::implConvertAndInsert( const Reference< XShapes
if( getShapeModel().mbIsSignatureLine )
{
- // Get the document signatures
- Reference< security::XDocumentDigitalSignatures > xSignatures(
- security::DocumentDigitalSignatures::createWithVersion(
- comphelper::getProcessComponentContext(), "1.2" ) );
-
- uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
- ZIP_STORAGE_FORMAT_STRING, mrDrawing.getFilter().getFileUrl(), embed::ElementModes::READ);
- SAL_WARN_IF(!xStorage.is(), "oox.vml", "No xStorage!");
-
- uno::Sequence< security::DocumentSignatureInformation > xSignatureInfo =
- xSignatures->verifyScriptingContentSignatures(xStorage, uno::Reference< io::XInputStream >());
-
OUString aGraphicUrl;
- for (int i=0; i<xSignatureInfo.getLength(); i++)
+ try
{
- // Try to find matching signature line image - if none exists that is fine,
- // then the signature line is not digitally signed.
- if (xSignatureInfo[i].SignatureLineId == getShapeModel().maSignatureId)
+ // Get the document signatures
+ Reference<security::XDocumentDigitalSignatures> xSignatures(
+ security::DocumentDigitalSignatures::createWithVersion(
+ comphelper::getProcessComponentContext(), "1.2"));
+
+ uno::Reference<embed::XStorage> xStorage
+ = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
+ ZIP_STORAGE_FORMAT_STRING, mrDrawing.getFilter().getFileUrl(),
+ embed::ElementModes::READ);
+ SAL_WARN_IF(!xStorage.is(), "oox.vml", "No xStorage!");
+
+ uno::Sequence<security::DocumentSignatureInformation> xSignatureInfo
+ = xSignatures->verifyScriptingContentSignatures(xStorage,
+ uno::Reference<io::XInputStream>());
+
+ for (int i = 0; i < xSignatureInfo.getLength(); i++)
{
- if (xSignatureInfo[i].SignatureIsValid)
- {
- // Signature is valid, use the 'valid' image
- SAL_WARN_IF(!xSignatureInfo[i].ValidSignatureLineImage.is(), "oox.vml", "No ValidSignatureLineImage!");
- aGraphicUrl = rFilter.getGraphicHelper().createGraphicObject(xSignatureInfo[i].ValidSignatureLineImage);
- }
- else
+ // Try to find matching signature line image - if none exists that is fine,
+ // then the signature line is not digitally signed.
+ if (xSignatureInfo[i].SignatureLineId == getShapeModel().maSignatureId)
{
- // Signature is invalid, use the 'invalid' image
- SAL_WARN_IF(!xSignatureInfo[i].InvalidSignatureLineImage.is(), "oox.vml", "No InvalidSignatureLineImage!");
- aGraphicUrl = rFilter.getGraphicHelper().createGraphicObject(xSignatureInfo[i].InvalidSignatureLineImage);
+ if (xSignatureInfo[i].SignatureIsValid)
+ {
+ // Signature is valid, use the 'valid' image
+ SAL_WARN_IF(!xSignatureInfo[i].ValidSignatureLineImage.is(), "oox.vml",
+ "No ValidSignatureLineImage!");
+ aGraphicUrl = rFilter.getGraphicHelper().createGraphicObject(
+ xSignatureInfo[i].ValidSignatureLineImage);
+ }
+ else
+ {
+ // Signature is invalid, use the 'invalid' image
+ SAL_WARN_IF(!xSignatureInfo[i].InvalidSignatureLineImage.is(), "oox.vml",
+ "No InvalidSignatureLineImage!");
+ aGraphicUrl = rFilter.getGraphicHelper().createGraphicObject(
+ xSignatureInfo[i].InvalidSignatureLineImage);
+ }
+ break;
}
- break;
}
}
+ catch (css::uno::Exception&)
+ {
+ // DocumentDigitalSignatures service not available.
+ // We continue by rendering the "unsigned" shape instead.
+ }
Reference< XShape > xShape;
if (!aGraphicUrl.isEmpty())
diff --git a/sw/qa/extras/ooxmlexport/data/signature-line-all-props-set.docx b/sw/qa/extras/ooxmlexport/data/signature-line-all-props-set.docx
new file mode 100644
index 000000000000..2f8401bf5ca1
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/signature-line-all-props-set.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
index 485ceebbcc09..4d6014dede64 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
@@ -1862,6 +1862,44 @@ DECLARE_OOXMLEXPORT_TEST(testTdf90789, "tdf90789.docx")
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(1), xPageCursor->getPage());
}
+DECLARE_OOXMLEXPORT_TEST(testSignatureLineShape, "signature-line-all-props-set.docx")
+{
+ uno::Reference<drawing::XShape> xSignatureLineShape = getShape(1);
+ uno::Reference<beans::XPropertySet> xPropSet(xSignatureLineShape, uno::UNO_QUERY);
+
+ bool bIsSignatureLine;
+ xPropSet->getPropertyValue("IsSignatureLine") >>= bIsSignatureLine;
+ CPPUNIT_ASSERT_EQUAL(true, bIsSignatureLine);
+
+ bool bShowSignDate;
+ xPropSet->getPropertyValue("SignatureLineShowSignDate") >>= bShowSignDate;
+ CPPUNIT_ASSERT_EQUAL(true, bShowSignDate);
+
+ bool bCanAddComment;
+ xPropSet->getPropertyValue("SignatureLineCanAddComment") >>= bCanAddComment;
+ CPPUNIT_ASSERT_EQUAL(true, bCanAddComment);
+
+ OUString aSignatureLineId;
+ xPropSet->getPropertyValue("SignatureLineId") >>= aSignatureLineId;
+ CPPUNIT_ASSERT_EQUAL(OUString("{0EBE47D5-A1BD-4C9E-A52E-6256E5C345E9}"), aSignatureLineId);
+
+ OUString aSuggestedSignerName;
+ xPropSet->getPropertyValue("SignatureLineSuggestedSignerName") >>= aSuggestedSignerName;
+ CPPUNIT_ASSERT_EQUAL(OUString("John Doe"), aSuggestedSignerName);
+
+ OUString aSuggestedSignerTitle;
+ xPropSet->getPropertyValue("SignatureLineSuggestedSignerTitle") >>= aSuggestedSignerTitle;
+ CPPUNIT_ASSERT_EQUAL(OUString("Farmer"), aSuggestedSignerTitle);
+
+ OUString aSuggestedSignerEmail;
+ xPropSet->getPropertyValue("SignatureLineSuggestedSignerEmail") >>= aSuggestedSignerEmail;
+ CPPUNIT_ASSERT_EQUAL(OUString("john at thefarmers.com"), aSuggestedSignerEmail);
+
+ OUString aSigningInstructions;
+ xPropSet->getPropertyValue("SignatureLineSigningInstructions") >>= aSigningInstructions;
+ CPPUNIT_ASSERT_EQUAL(OUString("Check the machines!"), aSigningInstructions);
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
More information about the Libreoffice-commits
mailing list