Page 1 of 1

Add image effects to Inline

PostPosted: Fri Oct 31, 2014 9:11 pm
by jarod
Hi all,
I am trying to add a shadow effect to an image using docx4j. I am creating the image using the following:

Code: Select all
// ByteBuffer myImage ...
byte[] bytes = myImage.array();
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage
            .createImagePart(wordMLPackage, bytes);
      int docPrId = 1;
      int cNvPrId = 2;
   
      Inline inline = imagePart.createImageInline(image_ID,
            image_description, docPrId, cNvPrId, true);


I tried using the tool to convert a docx into code and obtained the following code for setting my shapeProperties:

Code: Select all
public CTShapeProperties createIt() {

org.docx4j.dml.ObjectFactory dmlObjectFactory = new org.docx4j.dml.ObjectFactory();

CTShapeProperties shapeproperties = dmlObjectFactory.createCTShapeProperties();
    // Create object for effectLst
    CTEffectList effectlist = dmlObjectFactory.createCTEffectList();
    shapeproperties.setEffectLst(effectlist);
        // Create object for outerShdw
        CTOuterShadowEffect outershadoweffect = dmlObjectFactory.createCTOuterShadowEffect();
        effectlist.setOuterShdw(outershadoweffect);
            // Create object for srgbClr
            CTSRgbColor srgbcolor = dmlObjectFactory.createCTSRgbColor();
            outershadoweffect.setSrgbClr(srgbcolor);
>:- No code generator for package null
                // Create object for alpha (wrapped in JAXBElement)
                CTPositiveFixedPercentage positivefixedpercentage = dmlObjectFactory.createCTPositiveFixedPercentage();
                JAXBElement<org.docx4j.dml.CTPositiveFixedPercentage> positivefixedpercentageWrapped = dmlObjectFactory.createCTSRgbColorAlpha(positivefixedpercentage);
                srgbcolor.getEGColorTransform().add( positivefixedpercentageWrapped);
                    positivefixedpercentage.setVal( 70000 );
            outershadoweffect.setBlurRad( new Long(190500) );
            outershadoweffect.setDist( new Long(0) );
            outershadoweffect.setDir( new Integer(0) );
            outershadoweffect.setSx( new Integer(100000) );
            outershadoweffect.setSy( new Integer(100000) );
            outershadoweffect.setKx( new Integer(0) );
            outershadoweffect.setKy( new Integer(0) );
            outershadoweffect.setAlgn(org.docx4j.dml.STRectAlignment.TL);
    // Create object for xfrm
    CTTransform2D transform2d = dmlObjectFactory.createCTTransform2D();
    shapeproperties.setXfrm(transform2d);
        // Create object for ext
        CTPositiveSize2D positivesize2d = dmlObjectFactory.createCTPositiveSize2D();
        transform2d.setExt(positivesize2d);
            positivesize2d.setCx( 5731510 );
            positivesize2d.setCy( 2059598 );
        transform2d.setRot( new Integer(0) );
        // Create object for off
        CTPoint2D point2d = dmlObjectFactory.createCTPoint2D();
        transform2d.setOff(point2d);
            point2d.setY( 0 );
            point2d.setX( 0 );
    // Create object for ln
    CTLineProperties lineproperties = dmlObjectFactory.createCTLineProperties();
    shapeproperties.setLn(lineproperties);
        // Create object for noFill
        CTNoFillProperties nofillproperties = dmlObjectFactory.createCTNoFillProperties();
        lineproperties.setNoFill(nofillproperties);
    shapeproperties.setBwMode(org.docx4j.dml.STBlackWhiteMode.AUTO);
    // Create object for prstGeom
    CTPresetGeometry2D presetgeometry2d = dmlObjectFactory.createCTPresetGeometry2D();
    shapeproperties.setPrstGeom(presetgeometry2d);
        // Create object for avLst
        CTGeomGuideList geomguidelist = dmlObjectFactory.createCTGeomGuideList();
        presetgeometry2d.setAvLst(geomguidelist);
        presetgeometry2d.setPrst(org.docx4j.dml.STShapeType.RECT);

return shapeproperties;
}


The missing part is how to now set this shapeproperties on the inline.
I tried with:
Code: Select all
CTShapeProperties shapeProperties = createIt();
inline.getGraphic().getGraphicData().getPic().setSpPr(shapeproperties);


but the resulting word does not open...

Any help/suggestion is welcome :)

Thanks for reading

Re: Add image effects to Inline

PostPosted: Sat Nov 01, 2014 7:49 am
by jason
Unzip the resulting XML and compare it to the original. Or use System.out.println your MainDocumentPart's getXML

See which differences matter. If you have Visual Studio, it is helpful (taking care of the unzip/zip step). 7Zip is in some respects better, but its internal editor doesn't pretty print the XML

Maybe the problem has something to do with ">:- No code generator for package null" ?

Bear in mind that your problem might be elsewhere in the document?

Re: Add image effects to Inline

PostPosted: Mon Nov 03, 2014 8:41 pm
by jarod
Hi Jason,
thanks for the reply, I tried the unzipping and checking the content. The effectList parts are missing somehow in the result...
I also tried with the second method to avoid the "No code generator for package null" problem:
Code: Select all
public CTShapeProperties createShapeProperties() throws JAXBException {
      String openXML = "<pic:spPr><a:xfrm><a:off x=\"0\" y=\"0\"/><a:ext cx=\"5731510\" cy=\"2059940\"/></a:xfrm><a:prstGeom prst=\"rect\"><a:avLst/></a:prstGeom><a:ln><a:noFill/></a:ln><a:effectLst><a:outerShdw blurRad=\"190500\" algn=\"tl\" rotWithShape=\"0\"><a:srgbClr val=\"000000\"><a:alpha val=\"70000\"/></a:srgbClr></a:outerShdw></a:effectLst></pic:spPr>";
      CTShapeProperties shapeproperties = (CTShapeProperties) XmlUtils.unmarshalString(openXML);
      return shapeproperties;
   }

And calling this with:
Code: Select all
inline.getGraphic().getGraphicData().getPic()
            .setSpPr(createShapeProperties());

However it is still not working :cry:

If I leave out the shapeProperties, the document exports fine and everything is okay.

I even tried editing the document.xml in the zip file and copying the xml line in there directly, but the result is still not working.

I will try again a bit later... maybe I will see things clearer after a pause :|

If you have any working "public CTShapeProperties createShapeProperties()" function, I will be happy to try it out :)

Thanks for the help anyway!

Re: Add image effects to Inline

PostPosted: Wed Nov 05, 2014 4:15 pm
by jason
jarod wrote:I even tried editing the document.xml in the zip file and copying the xml line in there directly, but the result is still not working.


This is exactly what you need to do in situations like this .. you need to identify what it is about your docx which Word doesn't like. Once you have pinpointed that, addressing it is generally straightforward.

Keep at it :-)

Re: Add image effects to Inline

PostPosted: Wed Nov 05, 2014 8:53 pm
by jarod
Hi Jason,
I tried again around, I am closer, but still not there yet :)
From unzipping editing around, adding the following code just before the </pic:spPr> tag works and creates a border:
Code: Select all
<a:effectLst><a:outerShdw blurRad="190500" algn="tl" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="70000"/></a:srgbClr></a:outerShdw></a:effectLst>


So copy/paste of this by hand works. However when I create the same in code form as this:
Code: Select all
public CTEffectList createEffectList() throws JAXBException {
      String openXML = "<a:effectLst>"
               + "<a:outerShdw algn=\"tl\" blurRad=\"190500\" rotWithShape=\"0\">"
                   + "<a:srgbClr val=\"000000\">"
                       + "<a:alpha val=\"70000\"/>"
                   +"</a:srgbClr>"
               +"</a:outerShdw>"
           +"</a:effectLst>";
   CTEffectList effectlist = (CTEffectList)XmlUtils.unmarshalString(openXML);
      return effectlist;
   }

calling this with:
Code: Select all
inline.getGraphic().getGraphicData().getPic().getSpPr().setEffectLst(createEffectList());

I get an unmarchalling error:
Code: Select all
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 14; The prefix "a" for element "a:effectLst" is not bound.]


even though in the resulting xml, the xmlns:a is declared as:
Code: Select all
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"


The code version of this doesn't work either:
Code: Select all
public CTEffectList createIt() {

org.docx4j.dml.ObjectFactory dmlObjectFactory = new org.docx4j.dml.ObjectFactory();

CTEffectList effectlist = dmlObjectFactory.createCTEffectList();
    // Create object for outerShdw
    CTOuterShadowEffect outershadoweffect = dmlObjectFactory.createCTOuterShadowEffect();
    effectlist.setOuterShdw(outershadoweffect);
        // Create object for srgbClr
        CTSRgbColor srgbcolor = dmlObjectFactory.createCTSRgbColor();
        outershadoweffect.setSrgbClr(srgbcolor);
>:- No code generator for package null
            // Create object for alpha (wrapped in JAXBElement)
            CTPositiveFixedPercentage positivefixedpercentage = dmlObjectFactory.createCTPositiveFixedPercentage();
            JAXBElement<org.docx4j.dml.CTPositiveFixedPercentage> positivefixedpercentageWrapped = dmlObjectFactory.createCTSRgbColorAlpha(positivefixedpercentage);
            srgbcolor.getEGColorTransform().add( positivefixedpercentageWrapped);
                positivefixedpercentage.setVal( 70000 );
        outershadoweffect.setBlurRad( new Long(190500) );
        outershadoweffect.setDist( new Long(0) );
        outershadoweffect.setDir( new Integer(0) );
        outershadoweffect.setSx( new Integer(100000) );
        outershadoweffect.setSy( new Integer(100000) );
        outershadoweffect.setKx( new Integer(0) );
        outershadoweffect.setKy( new Integer(0) );
        outershadoweffect.setAlgn(org.docx4j.dml.STRectAlignment.TL);

return effectlist;
}

But as you said, the ">:- No code generator for package null" is a problem, even though I don't see what is missing ... (even if I comment it, it doesn't work, in case you wondered :) )

Any clue how to resolve the unmarchalling error?

Thanks for the help!

Re: Add image effects to Inline

PostPosted: Wed Nov 05, 2014 10:26 pm
by jarod
OKAY! I found the solution

Since the unmarchalling didn't work, I went back to the code version. The ">:- No code generator for package null" was definitely to look into.

This is the proper xmlns code to add an effect:
Code: Select all
<a:effectLst><a:outerShdw blurRad="190500" algn="tl" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="70000"/></a:srgbClr></a:outerShdw></a:effectLst>

and this is what was produced by the code:
Code: Select all
<a:effectLst><a:outerShdw blurRad="190500" algn="tl" rotWithShape="0"><a:srgbClr><a:alpha val="70000"/></a:srgbClr></a:outerShdw></a:effectLst>

So the value of srgbClr was missing (exactly where the package null is. So presume there must be a bug in the library somewhere. Anyway, I set the val with "srgbcolor.setVal(new byte[1]);" and it works now.

So here is the complete solution:
Create image and add effect:
Code: Select all
Inline inline = imagePart.createImageInline(image_ID,
            image_description, docPrId, cNvPrId, true);

      inline.getGraphic().getGraphicData().getPic().getSpPr().setEffectLst(createEffectList());

Which calls this function:
Code: Select all
   public CTEffectList createEffectList() throws JAXBException {
      org.docx4j.dml.ObjectFactory dmlObjectFactory = new org.docx4j.dml.ObjectFactory();
      CTEffectList effectlist = new CTEffectList();
       // Create object for outerShdw
       CTOuterShadowEffect outershadoweffect = new CTOuterShadowEffect();
       effectlist.setOuterShdw(outershadoweffect);
           // Create object for srgbClr
           CTSRgbColor srgbcolor = new CTSRgbColor();
           srgbcolor.setVal(new byte[1]);
           outershadoweffect.setSrgbClr(srgbcolor);
               // Create object for alpha (wrapped in JAXBElement)
               CTPositiveFixedPercentage positivefixedpercentage = new CTPositiveFixedPercentage();
               JAXBElement<org.docx4j.dml.CTPositiveFixedPercentage> positivefixedpercentageWrapped = dmlObjectFactory.createCTSRgbColorAlpha(positivefixedpercentage);
               srgbcolor.getEGColorTransform().add( positivefixedpercentageWrapped);
                   positivefixedpercentage.setVal( 70000 );
           outershadoweffect.setBlurRad( new Long(190500) );
           outershadoweffect.setDist( new Long(0) );
           outershadoweffect.setDir( new Integer(0) );
           outershadoweffect.setSx( new Integer(100000) );
           outershadoweffect.setSy( new Integer(100000) );
           outershadoweffect.setKx( new Integer(0) );
           outershadoweffect.setKy( new Integer(0) );
           outershadoweffect.setAlgn(org.docx4j.dml.STRectAlignment.TL);
           outershadoweffect.setRotWithShape(false);
         return effectlist;
      }


Thanks Jason for the help and motivation ;)
Cheers

Re: Add image effects to Inline

PostPosted: Thu Nov 06, 2014 12:49 am
by jarod
Just to complete my solution for other readers, here is my complete code for inserting a picture, resizing it to a percentage of the width and adding a shadow effect:
Code: Select all
      BinaryPartAbstractImage imagePart = BinaryPartAbstractImage
            .createImagePart(wordMLPackage, bytes);
      int docPrId = 1;
      int cNvPrId = 2;

      Inline inline = imagePart.createImageInline(image_ID,
            image_description, docPrId, cNvPrId, true);

      CTPositiveSize2D ctPositiveSize2D = new CTPositiveSize2D();
      long target_width = (long) (available_width * ((double) percentage_of_avaialable_width / 100)); // width that makes the picture fitperfectly
                                                                              
      // make room for the shadow effect if in a table to avoid cropping the shadow. (232 because of 231140 of the effectextent)
      if (isTable && (available_width - target_width < 232)){
         target_width = target_width - (232 - (available_width - target_width));
      }
      if (target_width * 1000 < inline.getExtent().getCx()) {
         long currentimageWidth = new BigDecimal(inline.getExtent().getCx() / 1000).longValue();
         double ratio = (double) currentimageWidth / (double) target_width;
         int width = (int) ((double) inline.getExtent().getCx() / (double) ratio);
         int heigth = (int) ((double) inline.getExtent().getCy() / (double) ratio);
         ctPositiveSize2D.setCx(width);
         ctPositiveSize2D.setCy(heigth);
         inline.setExtent(ctPositiveSize2D);
      }
      
      // Add shadow effect to picture
      inline.getGraphic().getGraphicData().getPic().getSpPr().setEffectLst(createEffectList());
                // we set the extent of the shadow effect
      inline.getEffectExtent().setL(171450);
      inline.getEffectExtent().setT(171450);
      inline.getEffectExtent().setR(231140);
      inline.getEffectExtent().setB(226060);

      R run = factory.createR();
      paragraph.getContent().add(run);
      Drawing drawing = factory.createDrawing();
      run.getContent().add(drawing);
      drawing.getAnchorOrInline().add(inline);   


with
Code: Select all
public CTEffectList createEffectList() throws JAXBException {
      org.docx4j.dml.ObjectFactory dmlObjectFactory = new org.docx4j.dml.ObjectFactory();
      CTEffectList effectlist = new CTEffectList();
      // Create object for outerShdw
      CTOuterShadowEffect outershadoweffect = new CTOuterShadowEffect();
      effectlist.setOuterShdw(outershadoweffect);
      // Create object for srgbClr
      CTSRgbColor srgbcolor = new CTSRgbColor();
      srgbcolor.setVal(new byte[1]);
      outershadoweffect.setSrgbClr(srgbcolor);
      // Create object for alpha (wrapped in JAXBElement)
      CTPositiveFixedPercentage positivefixedpercentage = new CTPositiveFixedPercentage();
      JAXBElement<org.docx4j.dml.CTPositiveFixedPercentage> positivefixedpercentageWrapped = dmlObjectFactory
            .createCTSRgbColorAlpha(positivefixedpercentage);
      srgbcolor.getEGColorTransform().add(positivefixedpercentageWrapped);
      positivefixedpercentage.setVal(70000);
      outershadoweffect.setBlurRad(new Long(190500));
      outershadoweffect.setDist(new Long(0));
      outershadoweffect.setDir(new Integer(0));
      outershadoweffect.setSx(new Integer(100000));
      outershadoweffect.setSy(new Integer(100000));
      outershadoweffect.setKx(new Integer(0));
      outershadoweffect.setKy(new Integer(0));
      outershadoweffect.setAlgn(org.docx4j.dml.STRectAlignment.TL);
      outershadoweffect.setRotWithShape(false);
      return effectlist;
   }


Have fun :D

Re: Add image effects to Inline

PostPosted: Tue Nov 11, 2014 1:41 pm
by jason
Thanks for keeping us all informed :-)

By the way your unmarshalling error:

Code: Select all
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 14; The prefix "a" for element "a:effectLst" is not bound.]


was because you were missing a namespace declaration for "a" in your string.