Kaydet (Commit) 2e68a146 authored tarafından Jacobo Aragunde Pérez's avatar Jacobo Aragunde Pérez

ooxml: Preserve the original picture in artistic effects

When Word applies an artistic effect, it creates two embedded files;
one contains the bitmap with the effect and the other one contains the
original bitmap to be able to undo the effect.

This patch reads the original bitmap, stores it in the shape grab bag
and saves it back to the docx file. Added unit tests too.

TODO: right now, if two effects point to the same original bitmap it
is stored twice, we should improve this.

Change-Id: Ia72034a257739abe4ffafa0f42b2a912e4bf9436
üst 642a252c
......@@ -26,6 +26,7 @@
#include <oox/drawingml/color.hxx>
#include <oox/helper/helper.hxx>
#include <oox/drawingml/embeddedwavaudiofile.hxx>
#include <oox/ole/oleobjecthelper.hxx>
namespace oox {
class GraphicHelper;
......@@ -70,6 +71,7 @@ struct ArtisticEffectProperties
OUString msName;
std::map< OUString, css::uno::Any >
maAttribs;
::oox::ole::OleObjectInfo mrOleObjectInfo; /// The original graphic as embedded object.
bool isEmpty() const;
......
......@@ -82,6 +82,7 @@ public:
private:
static int mnImageCounter;
static int mnWdpImageCounter;
/// To specify where write eg. the images to (like 'ppt', or 'word' - according to the OPC).
DocumentType meDocumentType;
......@@ -177,6 +178,7 @@ public:
void WriteShapeEffect( const OUString& sName, const css::uno::Sequence< css::beans::PropertyValue >& aEffectProps );
void WriteShape3DEffects( ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > rXPropSet );
void WriteArtisticEffect( ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > rXPropSet );
OString WriteWdpPicture( const ::com::sun::star::uno::Sequence< sal_Int8 >& rPictureData );
static void ResetCounters();
......
......@@ -608,7 +608,7 @@ css::beans::PropertyValue ArtisticEffectProperties::getEffect()
if( msName.isEmpty() )
return pRet;
css::uno::Sequence< css::beans::PropertyValue > aSeq( maAttribs.size() );
css::uno::Sequence< css::beans::PropertyValue > aSeq( maAttribs.size() + 1 );
sal_uInt32 i = 0;
for( std::map< OUString, css::uno::Any >::iterator it = maAttribs.begin(); it != maAttribs.end(); ++it )
{
......@@ -617,6 +617,12 @@ css::beans::PropertyValue ArtisticEffectProperties::getEffect()
i++;
}
if( mrOleObjectInfo.maEmbeddedData.hasElements() )
{
aSeq[i].Name = "OriginalGraphic";
aSeq[i].Value = uno::makeAny( mrOleObjectInfo.maEmbeddedData );
}
pRet.Name = msName;
pRet.Value = css::uno::Any( aSeq );
......
......@@ -333,9 +333,18 @@ ContextHandlerRef ArtisticEffectContext::onCreateContext(
sal_Int32 nElement, const AttributeList& rAttribs )
{
// containers
if( nElement == OOX_TOKEN( a14, imgLayer ) || nElement == OOX_TOKEN( a14, imgEffect ) )
if( nElement == OOX_TOKEN( a14, imgLayer ) )
{
if( rAttribs.hasAttribute( R_TOKEN( embed ) ) )
{
OUString aFragmentPath = getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( embed ), OUString() ) );
if( !aFragmentPath.isEmpty() )
getFilter().importBinaryData( maEffect.mrOleObjectInfo.maEmbeddedData, aFragmentPath );
}
return new ArtisticEffectContext( *this, maEffect );
}
if( nElement == OOX_TOKEN( a14, imgEffect ) )
return new ArtisticEffectContext( *this, maEffect );
// TODO: manage r:embed attribute in a14:imgLayer
// effects
maEffect.msName = ArtisticEffectProperties::getEffectString( nElement );
......
......@@ -119,10 +119,12 @@ namespace drawingml {
// not thread safe
int DrawingML::mnImageCounter = 1;
int DrawingML::mnWdpImageCounter = 1;
void DrawingML::ResetCounters()
{
mnImageCounter = 1;
mnWdpImageCounter = 1;
}
bool DrawingML::GetProperty( Reference< XPropertySet > rXPropSet, const OUString& aName )
......@@ -2590,6 +2592,7 @@ void DrawingML::WriteArtisticEffect( Reference< XPropertySet > rXPropSet )
Sequence< PropertyValue > aAttrs;
aEffect.Value >>= aAttrs;
sax_fastparser::FastAttributeList *aAttrList = mpFS->createAttrList();
OString sRelId;
for( sal_Int32 i=0; i < aAttrs.getLength(); ++i )
{
sal_Int32 nToken = ArtisticEffectProperties::getEffectToken( aAttrs[i].Name );
......@@ -2599,6 +2602,12 @@ void DrawingML::WriteArtisticEffect( Reference< XPropertySet > rXPropSet )
aAttrs[i].Value >>= nVal;
aAttrList->add( nToken, OString::number( nVal ).getStr() );
}
else if( aAttrs[i].Name == "OriginalGraphic" )
{
Sequence< sal_Int8 > aGraphicData;
aAttrs[i].Value >>= aGraphicData;
sRelId = WriteWdpPicture( aGraphicData );
}
}
mpFS->startElementNS( XML_a, XML_extLst, FSEND );
......@@ -2608,7 +2617,9 @@ void DrawingML::WriteArtisticEffect( Reference< XPropertySet > rXPropSet )
mpFS->startElementNS( XML_a14, XML_imgProps,
FSNS( XML_xmlns, XML_a14 ), "http://schemas.microsoft.com/office/drawing/2010/main",
FSEND );
mpFS->startElementNS( XML_a14, XML_imgLayer, FSEND );
mpFS->startElementNS( XML_a14, XML_imgLayer,
FSNS( XML_r, XML_embed), sRelId.getStr(),
FSEND );
mpFS->startElementNS( XML_a14, XML_imgEffect, FSEND );
sax_fastparser::XFastAttributeListRef xAttrList( aAttrList );
......@@ -2621,6 +2632,23 @@ void DrawingML::WriteArtisticEffect( Reference< XPropertySet > rXPropSet )
mpFS->endElementNS( XML_a, XML_extLst );
}
OString DrawingML::WriteWdpPicture( const Sequence< sal_Int8 >& rPictureData )
{
OUString sFileName = "media/hdphoto" + OUString::number( mnWdpImageCounter++ ) + ".wdp";
uno::Reference< io::XOutputStream > xOutStream =
mpFB->openFragmentStream( "word/" + sFileName,
"image/vnd.ms-photo" );
OUString sId;
xOutStream->writeBytes( rPictureData );
xOutStream->closeOutput();
sId = mpFB->addRelation( mpFS->getOutputStream(),
"http://schemas.microsoft.com/office/2007/relationships/hdphoto",
sFileName, false );
return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
}
}
}
......
......@@ -1341,9 +1341,13 @@ DECLARE_OOXMLEXPORT_TEST(testPictureEffectPreservation, "picture-effects-preserv
DECLARE_OOXMLEXPORT_TEST(testPictureArtisticEffectPreservation, "picture-artistic-effects-preservation.docx")
{
xmlDocPtr pXmlDoc = parseExport("word/document.xml");
if (!pXmlDoc)
xmlDocPtr pRelsDoc = parseExport("word/_rels/document.xml.rels");
if (!pXmlDoc || !pRelsDoc)
return;
uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(
comphelper::getComponentContext(m_xSFactory), maTempFile.GetURL());
// 1st picture: marker effect
assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:r/mc:AlternateContent/mc:Choice/w:drawing/wp:inline/a:graphic/"
"a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer/a14:imgEffect/"
......@@ -1354,6 +1358,13 @@ DECLARE_OOXMLEXPORT_TEST(testPictureArtisticEffectPreservation, "picture-artisti
"a14:artisticMarker",
"size", "80");
OUString sEmbedId1 = getXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:r/mc:AlternateContent/mc:Choice/w:drawing/"
"wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer",
"embed");
OUString sXmlPath = "/rels:Relationships/rels:Relationship[@Id='" + sEmbedId1 + "']";
OUString sFile = getXPath(pRelsDoc, OUStringToOString( sXmlPath, RTL_TEXTENCODING_UTF8 ), "Target");
CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName("word/" + sFile)));
// 2nd picture: pencil grayscale
assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:r/mc:AlternateContent/mc:Choice/w:drawing/wp:inline/a:graphic/"
"a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer/a14:imgEffect/"
......@@ -1364,6 +1375,13 @@ DECLARE_OOXMLEXPORT_TEST(testPictureArtisticEffectPreservation, "picture-artisti
"a14:artisticPencilGrayscale",
"pencilSize", "66");
OUString sEmbedId2 = getXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:r/mc:AlternateContent/mc:Choice/w:drawing/"
"wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer",
"embed");
sXmlPath = "/rels:Relationships/rels:Relationship[@Id='" + sEmbedId2 + "']";
sFile = getXPath(pRelsDoc, OUStringToOString( sXmlPath, RTL_TEXTENCODING_UTF8 ), "Target");
CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName("word/" + sFile)));
// 3rd picture: pencil sketch
assertXPath(pXmlDoc, "/w:document/w:body/w:p[3]/w:r/mc:AlternateContent/mc:Choice/w:drawing/wp:inline/a:graphic/"
"a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer/a14:imgEffect/"
......@@ -1374,6 +1392,13 @@ DECLARE_OOXMLEXPORT_TEST(testPictureArtisticEffectPreservation, "picture-artisti
"a14:artisticPencilSketch",
"pressure", "17");
OUString sEmbedId3 = getXPath(pXmlDoc, "/w:document/w:body/w:p[3]/w:r/mc:AlternateContent/mc:Choice/w:drawing/"
"wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer",
"embed");
sXmlPath = "/rels:Relationships/rels:Relationship[@Id='" + sEmbedId3 + "']";
sFile = getXPath(pRelsDoc, OUStringToOString( sXmlPath, RTL_TEXTENCODING_UTF8 ), "Target");
CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName("word/" + sFile)));
// 4th picture: light screen
assertXPath(pXmlDoc, "/w:document/w:body/w:p[4]/w:r/mc:AlternateContent/mc:Choice/w:drawing/wp:inline/a:graphic/"
"a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer/a14:imgEffect/"
......@@ -1384,16 +1409,37 @@ DECLARE_OOXMLEXPORT_TEST(testPictureArtisticEffectPreservation, "picture-artisti
"a14:artisticLightScreen",
"gridSize", "1");
OUString sEmbedId4 = getXPath(pXmlDoc, "/w:document/w:body/w:p[4]/w:r/mc:AlternateContent/mc:Choice/w:drawing/"
"wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer",
"embed");
sXmlPath = "/rels:Relationships/rels:Relationship[@Id='" + sEmbedId4 + "']";
sFile = getXPath(pRelsDoc, OUStringToOString( sXmlPath, RTL_TEXTENCODING_UTF8 ), "Target");
CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName("word/" + sFile)));
// 5th picture: watercolor sponge
assertXPath(pXmlDoc, "/w:document/w:body/w:p[5]/w:r/mc:AlternateContent/mc:Choice/w:drawing/wp:inline/a:graphic/"
"a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer/a14:imgEffect/"
"a14:artisticWatercolorSponge",
"brushSize", "4");
OUString sEmbedId5 = getXPath(pXmlDoc, "/w:document/w:body/w:p[5]/w:r/mc:AlternateContent/mc:Choice/w:drawing/"
"wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer",
"embed");
sXmlPath = "/rels:Relationships/rels:Relationship[@Id='" + sEmbedId5 + "']";
sFile = getXPath(pRelsDoc, OUStringToOString( sXmlPath, RTL_TEXTENCODING_UTF8 ), "Target");
CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName("word/" + sFile)));
// 6th picture: photocopy (no attributes)
assertXPath(pXmlDoc, "/w:document/w:body/w:p[6]/w:r/mc:AlternateContent/mc:Choice/w:drawing/wp:inline/a:graphic/"
"a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer/a14:imgEffect/"
"a14:artisticPhotocopy", 1);
OUString sEmbedId6 = getXPath(pXmlDoc, "/w:document/w:body/w:p[6]/w:r/mc:AlternateContent/mc:Choice/w:drawing/"
"wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/a:extLst/a:ext/a14:imgProps/a14:imgLayer",
"embed");
sXmlPath = "/rels:Relationships/rels:Relationship[@Id='" + sEmbedId6 + "']";
sFile = getXPath(pRelsDoc, OUStringToOString( sXmlPath, RTL_TEXTENCODING_UTF8 ), "Target");
CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName("word/" + sFile)));
}
DECLARE_OOXMLEXPORT_TEST(fdo77719, "fdo77719.docx")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment