Copied pictures from another word document are empty

196 Views Asked by At

I generate an SSRS report that I save as a Word document. That document contain pictures (charts as plat images). I don't really save the Word document, I have it open in memory.

I use then OpenXML to create another word document, in witch I need to copy the pictures from the SSRS report.

I try to get the drawings from the ssrs report, and insert them in the new word document

MainDocumentPart ssrsMainPart = ssrsDoc.MainDocumentPart;
var drawings = ssrsMainPart.Document.Descendants<Drawing>().ToList();

and then insert them in the new document

MainDocumentPart mainPart = wordDoc.MainDocumentPart;
Body body = mainPart.Document.Body;

for (int i = 0; i < 10; i++)
{
    if (i < drawings.Count())
    {
        var myDrawing = drawings.Skip(i).First();
        myDrawing.Remove();
        body.Append(myDrawing); 
        body.Append(new Paragraph());
    }
}

As result I get in the resulting document only the picture placeholders, of the same size as in ssrs Word Document, but the pictures are empty.

I tried to copy the ImageData as well, using the following code:

foreach (var e in ssrsDoc.MainDocumentPart.Document.Body.Elements())
{
    var clonedElement = e.CloneNode(true);
    clonedElement.Descendants<DocumentFormat.OpenXml.Drawing.Blip>().ToList().ForEach(blip =>
    {
        var newRelation = wordDoc.CopyImage(blip.Embed, ssrsDoc);
        blip.Embed = newRelation;
    });
    clonedElement.Descendants<DocumentFormat.OpenXml.Vml.ImageData>().ToList().ForEach(imageData =>
    {
        var newRelation = wordDoc.CopyImage(imageData.RelationshipId, ssrsDoc);
        imageData.RelationshipId = newRelation;
    });
    wordDoc.MainDocumentPart.Document.Body.AppendChild(clonedElement);
}

with CopyImage defined like this:

public static string CopyImage(this WordprocessingDocument newDoc, string relId, WordprocessingDocument org)
{
    var p = org.MainDocumentPart.GetPartById(relId) as ImagePart;
    var newPart = newDoc.MainDocumentPart.AddPart(p);
    newPart.FeedData(p.GetStream());
    return newDoc.MainDocumentPart.GetIdOfPart(newPart);
}

but after that I wasn't able to open anymore the document with Word 365, word reporting the doc as a broken one...

1

There are 1 best solutions below

0
user20568 On

I'd comment here, but I don't have enough reputation. Anyway I've discovered a similar issue when attempting to spool out image file names. After getting the blip, and then obtaining its relationship ID, and then retrieving the URI, I've discovered that none of these URIs matches images.

For example, I'll have a Word file with 16 images. When manually deconstructing the .docx file as a .zip file, I can locate the images in /word/media/. They'll be sequentially numbered from image1.jpeg all the way to image16.jpeg, but those aren't returned through programming.

The OpenXML SDK appears to return a mishmash of varying image numbers, some with 4 digits. Base 10 counting doesn't reach any 4 digit numbers between 1 and 16. There's something going wrong in the OpenXML SDK, and rolling back to earlier versions hasn't helped me with my problem.

EDIT: By way of a potential answer: you can use OpenXML Power Tools instead. Converting the document source via their HTMLConverter permits you to get the correct image file names. This VB.net code should be convertable into C to get you the results you need...

' Prepares to convert a word document...
Dim conSettings As New HtmlConverterSettings
With conSettings
    ' Permits the system to use international numbering formats...
    .RestrictToSupportedNumberingFormats = False
    ' Uses INLINE CSS so XAML conversion can style individual items...
    .FabricateCssClasses = False
    ' Permits the system to handle the image information...
    .ImageHandler = Function(imageInfo)

    ' Loads directory information...
    Dim localDirInfo As DirectoryInfo = New DirectoryInfo("img")
    ' Prepares to store an image file name...
    Dim strImage As String = ""
    ' Responds if a local directory is not available for saving images...
    If Not localDirInfo.Exists Then
        ' Constructs the local directory if it wasn't there...
        localDirInfo.Create()
    End If
    ' Counts images that were found in the Word / Open XML document package...
    intImages += 1
    ' Gets the image file type...
    Dim strType As String = imageInfo.ContentType.Split("/"c)(1).ToLower()
    ' Prepares to set the image file format...
    Dim imageFormat As ImageFormat = Nothing
    ' Responds based on the image file type...
    Select Case strType
        Case "png"  ' maps PNG images to GIF format
            strType = "gif"
            imageFormat = ImageFormat.Gif
        Case "gif"
            imageFormat = ImageFormat.Gif
        Case "bmp"
            imageFormat = ImageFormat.Bmp
        Case "jpg"  ' maps JPG images to JPEG format
            strType = "jpeg"
            imageFormat = ImageFormat.Jpeg
        Case "jpeg"
            imageFormat = ImageFormat.Jpeg
        Case "tiff"  ' maps TIFF images to GIF format
            strType = "gif"
            imageFormat = ImageFormat.Gif
        Case "x-wmf"  ' maps X-WMF images to WMF format
            strType = "wmf"
            imageFormat = ImageFormat.Wmf
    End Select
    ' Only operates if an image format exists...
    If imageFormat Is Nothing Then
        ' Stops processing...
        Return Nothing
    Else
        ' Creates the file path for the 
        strImage = "img/image" & intImages.ToString() & "." & strType
    End If
    ' Attempts to avoid crashing...
    Try
        ' Attempts to save the image outside the Word / Open XML package file...
        imageInfo.Bitmap.Save(strImage, imageFormat)
    Catch __unusedExternalException1__ As System.Runtime.InteropServices.ExternalException
        ' Stops processing...
        Return Nothing
    End Try
    ' Produces the resultant HTML image tag...
    Dim img As XElement = New XElement(Xhtml.img, New XAttribute(NoNamespace.src, strImage), imageInfo.ImgStyleAttribute, If(imageInfo.AltText IsNot Nothing, New XAttribute(NoNamespace.alt, imageInfo.AltText), Nothing))
    ' Attempts to store the resultant HTML image tag for later use...
    lstImages.Add(img)
    ' Returns the resultant HTML image tag...
    Return img
End Function

End With
' Loads content into the system as a file stream...
docStream = New FileStream(strFile, FileMode.Open) 'OrCreate)
' Collects the document...
Dim wdDoc As WordprocessingDocument
wdDoc = WordprocessingDocument.Open(docStream, True)
' Attempts to read the file to convert it into a flow document...
With wdDoc
    ' Only operates if the document body exists...
    If Not IsNothing(wdDoc.MainDocumentPart) Then
        ' Collects the document body...
        wdBody = wdDoc.MainDocumentPart
        ' Attempts to convert the document to HTML...
        strHTMLOutput = OpenXmlPowerTools.HtmlConverter.ConvertToHtml(wdDoc, conSettings).ToString
    End If
End With
' Closes the document...
docStream.Close()

When you run the code, it will process in the image handler, store files in the local directory /img/ and you should also get a List of images.