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 <[email protected]> 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@4@5l@4@11@9@11@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 <[email protected]> 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 <[email protected]> Tested-by: Samuel Mehrbrodt <[email protected]> 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("[email protected]"), 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: */ _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
