This project has moved. For the latest updates, please go here.

Paragraph.Pictures always 0?

Sep 12, 2014 at 5:02 PM
I'm attempting to insert the contents of a whole document, source document, into a table cell of my template document.

The source document has a table, an image, and some text.

I'm able to insert the table and text from my source document into the template, but I'm unable to insert the image.

I believe all of the images get moved over in the last foreach loop, but I assumed I'd have to grab each one of those images and place them into the paragraph of the template document as I am iterating through and adding paragraphs to the cell, however in my source document I don't know which paragraph should get the pictures because paragraph.Pictures.Any() is never true.

Thoughts on where I'm going wrong?
using (var documentToAppend = DocX.Load(pathDocument))
                    {                      
                        foreach (var paragraph in documentToAppend.Paragraphs)
                        {
                            if (paragraph.Pictures.Any())
                            {
                                //code never reached
                            }
                            if (!paragraph.ParentContainer.ToString().Equals("Cell"))
                            {
                                insertedTable.Rows[0].Cells[0].Paragraphs.Last().InsertParagraphAfterSelf(paragraph);
                            }
                            if (paragraph.FollowingTable != null)
                            {
                                insertedTable.Rows[0].Cells[0].Paragraphs.Last().InsertTableBeforeSelf(paragraph.FollowingTable);
                            }
                        }
                        foreach (var imageToAppened in documentToAppend.Images)
                        {
                            document.AddImage(imageToAppened.GetStream(FileMode.Open, FileAccess.Read));
                        }
                    }
Sep 16, 2014 at 9:45 AM
Perhaps you could try using paragraph.Pictures.Count > 0 ? The .Any method is not available in my (test) solution so I can't test that (I'm not sure why, I'm guessing it's because of the C# version I'm using).
After adding an image to the document, you have to create a picture and use AppendPicture or InsertPicture to add it to a paragraph so the image will show up in your document:
Novacode.Image img = document.AddImage(@image);
Picture pic = img.CreatePicture();
Paragraph p = document.InsertParagraph();
p.AppendPicture(pic);
Hope this helps.
Sep 16, 2014 at 1:46 PM
@Annika89 Picture.Count is also 0.

I want to use the code you provided...I figured I'd so something exactly like that. However I can't figure out which paragraph would have images in them when Picture.Count is always zero even for the paragraphs where I know a picture should be placed.
Sep 17, 2014 at 9:16 AM
Edited Sep 17, 2014 at 9:17 AM
I think I know what's going on now. The .Pictures list consists of pictures inside a <w:drawing> element, I suspect your image is inside a <v:shape> tag (you can check if that's true by inspecting the document.xml file from your document) and "can't" be found.

I'll see if I can reproduce this issue (and then I'll try to come up with a patch to fix this).
Sep 17, 2014 at 1:25 PM
@Annika89 From my document.xml. Below is my problematic paragraph with a shape element defined inside of the picture. Just to make sure we are on the same page, is this what you were suggesting in your last post that might be the problem?
<w:p w:rsidR="00707846" w:rsidRDefault="00053228" w:rsidP="00053228">
      <w:pPr>
        <w:spacing w:after="180" w:line="240" w:lineRule="auto"/>
        <w:ind w:left="720"/>
        <w:jc w:val="center"/>
        <w:rPr>
          <w:rFonts w:ascii="Calibri" w:hAnsi="Calibri"/>
          <w:color w:val="000000"/>
          <w:sz w:val="28"/>
          <w:u w:color="000000"/>
        </w:rPr>
      </w:pPr>
      <w:bookmarkStart w:id="0" w:name="_GoBack"/>
      <w:r>
        <w:pict>
          <v:shape id="_x0000_i1025" style="width:159pt;height:74.25pt" coordsize="" o:spt="100" adj="0,,0" path="" filled="f" stroked="f">
            <v:stroke joinstyle="miter"/>
            <v:imagedata r:id="rId5" o:title=""/>
            <v:formulas/>
            <v:path o:connecttype="segments"/>
          </v:shape>
        </w:pict>
      </w:r>
      <w:bookmarkEnd w:id="0"/>
    </w:p>
Sep 18, 2014 at 1:49 PM
Thank you for posting your document.xml content, that is indeed what I thought the problem might be.
The Paragraph.Pictures won't find your image because it's not inside a w:drawing tag.

I did find a way to make sure the pictures are added to the list, by editing the Paragraph.Pictures method (not sure if this is the best way to go about this, I'm not that familiar with LINQ):
public List<Picture> Pictures
{
    get
    {
        List<Picture> pictures =
        (
            from p in Xml.Descendants()
            where (p.Name.LocalName == "drawing")
            let id =
            (
                from e in p.Descendants()
                where e.Name.LocalName.Equals("blip")
                select e.Attribute(XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships")).Value
            ).SingleOrDefault()
            where id != null
            let img = new Image(Document, mainPart.GetRelationship(id))
            select new Picture(Document, p, img)
        ).ToList();
        
        List<Picture> shapes =
        (
            from p in Xml.Descendants()
            where (p.Name.LocalName == "pict")
            let id =
            (
                from e in p.Descendants()
                where e.Name.LocalName.Equals("imagedata")
                select e.Attribute(XName.Get("id", "http://schemas.openxmlformats.org/officeDocument/2006/relationships")).Value
            ).SingleOrDefault()
            where id != null
            let img = new Image(Document, mainPart.GetRelationship(id))
            select new Picture(Document, p, img)
        ).ToList();
        
        foreach (Picture p in shapes)
            pictures.Add(p);
        
        return pictures;
    }
}
And in Pictures.cs it's necessary to change some parts as well:
internal Picture(DocX document, XElement i, Image img):base(document, i)
{
    picture_rels = new Dictionary<PackagePart, PackageRelationship>();
    
    this.img = img;

    this.id =
    (
        from e in Xml.Descendants()
        where e.Name.LocalName.Equals("blip")
        select e.Attribute(XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships")).Value
    ).SingleOrDefault();

    if (this.id == null)
    {
        this.id =
        (
            from e in Xml.Descendants()
            where e.Name.LocalName.Equals("imagedata")
            select e.Attribute(XName.Get("id", "http://schemas.openxmlformats.org/officeDocument/2006/relationships")).Value
        ).SingleOrDefault();
    }

    this.name = 
    (
        from e in Xml.Descendants()
        let a = e.Attribute(XName.Get("name"))
        where (a != null)
        select a.Value
    ).FirstOrDefault();
    
    if (this.name == null)
    {
        this.name = 
        (
            from e in Xml.Descendants()
            let a = e.Attribute(XName.Get("title"))
            where (a != null)
            select a.Value
        ).FirstOrDefault();
    }
  
    this.descr =
    (
        from e in Xml.Descendants()
        let a = e.Attribute(XName.Get("descr"))
        where (a != null)
        select a.Value
    ).FirstOrDefault();

    this.cx = 
    (
        from e in Xml.Descendants()
        let a = e.Attribute(XName.Get("cx"))
        where (a != null)
        select int.Parse(a.Value)
    ).FirstOrDefault();
    
    if (this.cx == null || this.cx == 0)
    {
        XAttribute style = 
        (
            from e in Xml.Descendants()
            let a = e.Attribute(XName.Get("style"))
            where (a != null)
            select a
        ).FirstOrDefault();
        
        string fromWidth = style.Value.Substring(style.Value.IndexOf("width:") + 6);
        var widthInt = ((double.Parse((fromWidth.Substring(0, fromWidth.IndexOf("pt;"))).Replace(".", ","))) / 72.0) * 914400;
        cx = System.Convert.ToInt32(widthInt);
    }

    this.cy = 
    (
        from e in Xml.Descendants()
        let a = e.Attribute(XName.Get("cy"))
        where (a != null)
        select int.Parse(a.Value)
    ).FirstOrDefault();
    
    if (this.cy == null || this.cy == 0)
    {
        XAttribute style = 
        (
            from e in Xml.Descendants()
            let a = e.Attribute(XName.Get("style"))
            where (a != null)
            select a
        ).FirstOrDefault();
        
        string fromHeight = style.Value.Substring(style.Value.IndexOf("height:") + 7);
        var heightInt = ((double.Parse((fromHeight.Substring(0, fromHeight.IndexOf("pt;"))).Replace(".", ","))) / 72.0) * 914400;
        cy = System.Convert.ToInt32(heightInt);
    }

    this.xfrm =
    (
        from d in Xml.Descendants()
        where d.Name.LocalName.Equals("xfrm")
        select d
    ).SingleOrDefault();

    this.prstGeom =
    (
        from d in Xml.Descendants()
        where d.Name.LocalName.Equals("prstGeom")
        select d
    ).SingleOrDefault();

    if (xfrm != null)
        this.rotation = xfrm.Attribute(XName.Get("rot")) == null ? 0 : uint.Parse(xfrm.Attribute(XName.Get("rot")).Value);
}
It's not pretty, but it worked for me. However, even with this I couldn't figure out how to get the images into another DocX document.
Sep 18, 2014 at 2:07 PM
I'll give your code above a shot, and let you know how it works, thanks!

To move the images from one .docx to the other I was using the following:
foreach (var imageToAppened in documentToAppend.Images)
{
    document.AddImage(imageToAppened.GetStream(FileMode.Open, FileAccess.Read));
}
That appeared to move them over.
Sep 18, 2014 at 4:39 PM
@Annika89 - Your code worked like a charm! Just one minor bug, replace:
 var heightInt = ((double.Parse((fromHeight.Substring(0, fromHeight.IndexOf("pt;"))).Replace(".", ","))) / 72.0) * 914400;
with
 var heightInt = ((double.Parse((fromHeight.Substring(0, fromHeight.IndexOf("pt"))).Replace(".", ","))) / 72.0) * 914400;
In my example doc height does not have a ;
<v:shape id="_x0000_i1025" style="width:159pt;height:74.25pt" coordsize="" o:spt="100" adj="0,,0" path="" filled="f" stroked="f">
Would you submit a patch so this gets into the trunk?
Sep 18, 2014 at 4:44 PM
For the sake of completeness my working code with @Annika89's patch:
using (var documentToAppend = DocX.Load(pathDocument))
{                      
    foreach (var paragraph in documentToAppend.Paragraphs)
    {                                                        
        if (paragraph.Pictures.Any())
        {
            //grab the image from the document to appened and place it in this paragraph
            foreach (var pictureFromDocumentToAppend in paragraph.Pictures)
            {
                var imageToAppend = documentToAppend.Images.FirstOrDefault(x => x.FileName == pictureFromDocumentToAppend.FileName);
                if (imageToAppend != null)
                {                    
                    Image appendedImage = document.AddImage(imageToAppend.GetStream(FileMode.Open, FileAccess.Read));
                    var pictureToAppend = appendedImage.CreatePicture();
                                        
                    insertedTable.Rows[0].Cells[0].Paragraphs.Last().InsertParagraphAfterSelf("");
                    insertedTable.Rows[0].Cells[0].Paragraphs.Last().Alignment = paragraph.Alignment;
                    insertedTable.Rows[0].Cells[0].Paragraphs.Last().InsertPicture(pictureToAppend);                 
                }
            }
        }
        else
        {
            if (!paragraph.ParentContainer.ToString().Equals("Cell"))
            {
                insertedTable.Rows[0].Cells[0].Paragraphs.Last().InsertParagraphAfterSelf(paragraph);
            }
            if (paragraph.FollowingTable != null)
            {
                insertedTable.Rows[0].Cells[0].Paragraphs.Last().InsertTableBeforeSelf(paragraph.FollowingTable);
            }
        }
    }    
}
Sep 19, 2014 at 1:25 PM
Edited Sep 19, 2014 at 1:25 PM
Thanks for correcting that bug :) I just uploaded a patch (including your correction).
I'm glad you got your code working with these changes!
Oct 6, 2014 at 4:53 PM
@Annika89, I think I might have found a bug in in Pictures.cs, but would like a second set of eyes on it just to make sure.

The lines
var widthInt = ((double.Parse((fromWidth.Substring(0, fromWidth.IndexOf("pt;"))).Replace(".", ","))) / 72.0) * 914400;
var heightInt = ((double.Parse((fromHeight.Substring(0, fromHeight.IndexOf("pt"))).Replace(".", ","))) / 72.0) * 914400;
Why is there a Replace in these statements?

In my testing I have an image that has a height 263.9677pt, if I call
double.Parse((fromHeight.Substring(0, fromHeight.IndexOf("pt"))).Replace(".", ","))
I'll get 2639677.0. If I continue with the calculations heightInt will equal 33523897900. Trying to convert that number to an int results in a System.OverflowException.

If I remove the Replace the operations work as expected, but wanted to check why the replace was thought to be needed in the first place.
Oct 7, 2014 at 9:11 AM
Edited Oct 7, 2014 at 9:58 AM
Ah, I guess it's based on system settings or CultureInfo or something, because for me the *100 occurred without the Replace.
I use a comma as a decimal separator and a dot as "grouping", is that the other way around for you perhaps?
I'll see if I can resolve this issue so it results in the right value for both of us.

Edit: Does it work if you change the lines with Replace to this:
var widthInt = ((double.Parse((fromWidth.Substring(0, fromWidth.IndexOf("pt"))), new CultureInfo("en-US"))) / 72.0) * 914400;
(...)
var heightInt = ((double.Parse((fromHeight.Substring(0, fromHeight.IndexOf("pt"))), new CultureInfo("en-US"))) / 72.0) * 914400;
You'll need to add
using System.Globalization;
to use the CultureInfo statements. I no longer get the OverflowException like this.

I'll upload/update the patch if this works for you as well.
Oct 20, 2014 at 2:33 PM
Found another one where pictures do not get populated, shape inside an object
    <w:p w:rsidR="00723F7C" w:rsidRPr="00723F7C" w:rsidRDefault="00723F7C" w:rsidP="00723F7C">
      <w:pPr>
        <w:tabs>
          <w:tab w:val="left" w:pos="1800"/>
        </w:tabs>
        <w:spacing w:before="60"/>
        <w:ind w:left="1260"/>
        <w:rPr>
          <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/>
          <w:sz w:val="20"/>
        </w:rPr>
      </w:pPr>
      <w:r w:rsidRPr="00723F7C">
        <w:rPr>
          <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/>
          <w:sz w:val="20"/>
        </w:rPr>
        <w:object w:dxaOrig="7560" w:dyaOrig="3060">
          <v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
            <v:stroke joinstyle="miter"/>
            <v:formulas>
              <v:f eqn="if lineDrawn pixelLineWidth 0"/>
              <v:f eqn="sum @0 1 0"/>
              <v:f eqn="sum 0 0 @1"/>
              <v:f eqn="prod @2 1 2"/>
              <v:f eqn="prod @3 21600 pixelWidth"/>
              <v:f eqn="prod @3 21600 pixelHeight"/>
              <v:f eqn="sum @0 0 1"/>
              <v:f eqn="prod @6 1 2"/>
              <v:f eqn="prod @7 21600 pixelWidth"/>
              <v:f eqn="sum @8 21600 0"/>
              <v:f eqn="prod @7 21600 pixelHeight"/>
              <v:f eqn="sum @10 21600 0"/>
            </v:formulas>
            <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
            <o:lock v:ext="edit" aspectratio="t"/>
          </v:shapetype>
          <v:shape id="_x0000_i1025" type="#_x0000_t75" style="width:348.75pt;height:162pt" o:ole="" fillcolor="window">
            <v:imagedata r:id="rId8" o:title="" cropright="8274f"/>
          </v:shape>
          <o:OLEObject Type="Embed" ProgID="Word.Picture.8" ShapeID="_x0000_i1025" DrawAspect="Content" ObjectID="_1468650721" r:id="rId9"/>
        </w:object>
      </w:r>
    </w:p>