Inserting Image into DocX using OpenXML and setting the size

42,825

Solution 1

The sizes, in EMUs (English Metric Unit -- read this for a good explanation), are set in the Extents (the Cx and Cy). In order to get a pic into a DocX I usually do it like so:

  1. Get the image's dimensions and resolution
  2. Compute the image's width in EMUs: wEmu = imgWidthPixels / imgHorizontalDpi * emuPerInch
  3. Compute the image's height in EMUs: hEmu = imgHeightPixels / imgVerticalDpi * emuPerInch
  4. Compute the max page width in EMUs (I've found that if the image is too wide, it won't show)
  5. If the image's width in EMUs is greater than the max page width's, I scale the image's width and height so that the width of image is equal to that of the page (when I say page, I'm referring to the "usable" page, that is minus the margins):

    var ratio = hEmu / wEmu;
    wEmu = maxPageWidthEmu;
    hEmu = wEmu * ratio;

  6. I then use the width as the value of Cx and height as the value of Cy, both in the DW.Extent and in the A.Extents (of the A.Transform2D of the PIC.ShapeProperties).

Note that the emuPerInch value is 914400.
I have this running (in a service) but I don't have the code with me right now.

UPDATE

This is the code I use:

var img = new BitmapImage(new Uri(fileName, UriKind.RelativeOrAbsolute));
var widthPx = img.PixelWidth;
var heightPx = img.PixelHeight;
var horzRezDpi = img.DpiX;
var vertRezDpi = img.DpiY;
const int emusPerInch = 914400;
const int emusPerCm = 360000;
var widthEmus = (long)(widthPx / horzRezDpi * emusPerInch);
var heightEmus = (long)(heightPx / vertRezDpi * emusPerInch);
var maxWidthEmus = (long)(maxWidthCm * emusPerCm);
if (widthEmus > maxWidthEmus) {
  var ratio = (heightEmus * 1.0m) / widthEmus;
  widthEmus = maxWidthEmus;
  heightEmus = (long)(widthEmus * ratio);
}

In my case, my page's measures are in cm, hence the emusPerCm you see above.

UPDATE 2 (to answer @andw)

To have the file locked during the minimum possible time, open it with a FileStream and pass the resulting stream to the StreamSource property of the BitmapImage:

var img = new BitmapImage();
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
    img.BeginInit();
    img.StreamSource = fs;
    img.EndInit();
}
// The file is now unlocked
var widthPx = img.PixelWidth;
...

Solution 2

You can give to the image its original size this way:

First you get the width and height of the file:

int iWidth = 0;
int iHeight = 0;
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap("yourFilePath"))
{
     iWidth = bmp.Width;
     iHeight = bmp.Height;
}

Then convert the pixels to EMUs this way:

iWidth = (int)Math.Round((decimal)iWidth * 9525);
iHeight = (int)Math.Round((decimal)iHeight * 9525);

And finally give the values when opening your file, I mean in this line of your code:

new DW.Extent() { Cx = 990000L, Cy = 792000L },

replace Cx and Cy with your calculated values so it looks like this:

new DW.Extent() { Cx = iWidth, Cy = iHeight },

Notice there is two lines in your code where you have to give the calculated values.

Then you have your image in its original size, hope this helps someone.

Solution 3

Since this is one of the first hits when searching for images and OpenXml, and the world has turned a bit further I'd like to share @ssarabando's solution for calculating the EMUs adapted for .net core where System.Drawing is not available, using ImageSharp (still beta as of nov 2018):

const int emusPerInch = 914400;
const int emusPerCm = 360000;

long widthEmus;
long heightEmus;
Image<Rgba32> img = Image.Load(sourceStream, new PngDecoder());

switch (img.MetaData.ResolutionUnits)
{
    case PixelResolutionUnit.PixelsPerCentimeter :
        widthEmus = (long)(img.Width / img.MetaData.HorizontalResolution * emusPerCm);
        heightEmus = (long)(img.Height / img.MetaData.VerticalResolution * emusPerCm);
        break;
    case PixelResolutionUnit.PixelsPerInch:
        widthEmus = (long)(img.Width / img.MetaData.HorizontalResolution * emusPerInch);
        heightEmus = (long)(img.Height / img.MetaData.VerticalResolution * emusPerInch);
        break;
    case PixelResolutionUnit.PixelsPerMeter:
        widthEmus = (long)(img.Width / img.MetaData.HorizontalResolution * emusPerCm * 100);
        heightEmus = (long)(img.Height / img.MetaData.VerticalResolution * emusPerCm * 100);
        break;
    default:
        widthEmus = 2000000;
        heightEmus = 2000000;
        break;
}

Hope it will save someone's time.

Solution 4

This code works for me.

Source: http://msdn.microsoft.com/en-us/library/office/bb497430(v=office.15).aspx

    public static void Do()
    {
        string filename = @"c:\temp\m.docx";
        byte[] reportData = GetWordReport();
       // File.WriteAllBytes(filename, reportData);
        //MessageBox.Show("File " + filename + " created");
    }

    private static byte[] GetWordReport()
    {
       // using (MemoryStream stream = new MemoryStream())
       // {
            //var template = GetTemplateData();
            //stream.Write(template, 0, template.Length);
            using (WordprocessingDocument docx = WordprocessingDocument.Open(@"c:\temp\m.docx", true))
            {
                // Some changes on docx
                docx.MainDocumentPart.Document = GenerateMainDocumentPart(6,4);

                var imagePart = docx.MainDocumentPart.AddNewPart<ImagePart>("image/jpeg", "rIdImagePart1");
                GenerateImagePart(imagePart);
            }
          //  stream.Seek(0, SeekOrigin.Begin);
           // return stream.ToArray();
       // }
        return null;
    }

    private static byte[] GetTemplateData()
    {
        using (MemoryStream targetStream = new MemoryStream())
        using (BinaryReader sourceReader = new BinaryReader(File.Open(@"c:\temp\m_2.docx", FileMode.Open)))
        {
            byte[] buffer = new byte[4096];

            int num = 0;
            do
            {
                num = sourceReader.Read(buffer, 0, 4096);
                if (num > 0)
                    targetStream.Write(buffer, 0, num);
            }
            while (num > 0);
            targetStream.Seek(0, SeekOrigin.Begin);
            return targetStream.ToArray();
        }
    }

    private static void GenerateImagePart(OpenXmlPart part)
    {
        using (Stream imageStream = File.Open(@"c:\temp\image002.jpg", FileMode.Open))
        {
            part.FeedData(imageStream);
        }
    }

    private static Document GenerateMainDocumentPart(int cx,int cy)
    {
        long LCX = cx*261257L;
        long LCY = cy*261257L;




        var element =
            new Document(
                new Body(
                    new Paragraph(
                        new Run(
                            new RunProperties(
                                new NoProof()),
                            new Drawing(
                                new wp.Inline(
                                    new wp.Extent() { Cx = LCX, Cy = LCY },
                                    new wp.EffectExtent() { LeftEdge = 0L, TopEdge = 19050L, RightEdge = 0L, BottomEdge = 0L },
                                    new wp.DocProperties() { Id = (UInt32Value)1U, Name = "Picture 0", Description = "Forest Flowers.jpg" },
                                    new wp.NonVisualGraphicFrameDrawingProperties(
                                        new a.GraphicFrameLocks() { NoChangeAspect = true }),
                                    new a.Graphic(
                                        new a.GraphicData(
                                            new pic.Picture(
                                                new pic.NonVisualPictureProperties(
                                                    new pic.NonVisualDrawingProperties() { Id = (UInt32Value)0U, Name = "Forest Flowers.jpg" },
                                                    new pic.NonVisualPictureDrawingProperties()),
                                                new pic.BlipFill(
                                                    new a.Blip() { Embed = "rIdImagePart1", CompressionState = a.BlipCompressionValues.Print },
                                                    new a.Stretch(
                                                        new a.FillRectangle())),
                                                new pic.ShapeProperties(
                                                    new a.Transform2D(
                                                        new a.Offset() { X = 0L, Y = 0L },
                                                        new a.Extents() { Cx = LCX, Cy = LCY }),
                                                    new a.PresetGeometry(
                                                        new a.AdjustValueList()
                                                    ) { Preset = a.ShapeTypeValues.Rectangle }))
                                        ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
                                ) { DistanceFromTop = (UInt32Value)0U, DistanceFromBottom = (UInt32Value)0U, DistanceFromLeft = (UInt32Value)0U, DistanceFromRight = (UInt32Value)0U }))
                    ) { RsidParagraphAddition = "00A2180E", RsidRunAdditionDefault = "00EC4DA7" },
                    new SectionProperties(
                        new PageSize() { Width = (UInt32Value)11906U, Height = (UInt32Value)16838U },
                        new PageMargin() { Top = 1440, Right = (UInt32Value)1800U, Bottom = 1440, Left = (UInt32Value)1800U, Header = (UInt32Value)851U, Footer = (UInt32Value)992U, Gutter = (UInt32Value)0U },
                        new Columns() { Space = ((UInt32Value)425U).ToString() },
                        new DocGrid() { Type = DocGridValues.Lines, LinePitch = 312 }
                    ) { RsidR = "00A2180E", RsidSect = "00A2180E" }));
        return element;
    }

Solution 5

This is what works for me in Net Core and implemented here.

long iWidth = 0;
long iHeight = 0;
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(fullPathToImageFile))
{
    iWidth = bmp.Width;
    iHeight = bmp.Height;
}            

iWidth = (long)Math.Round((decimal)iWidth * 9525);
iHeight = (long)Math.Round((decimal)iHeight * 9525);

double maxWidthCm = 17.4; // Our current margins gives us 17.4cm of space
const int emusPerCm = 360000;
long maxWidthEmus = (long)(maxWidthCm * emusPerCm);
if (iWidth > maxWidthEmus) {
    var ratio = (iHeight * 1.0m) / iWidth;
    iWidth = maxWidthEmus;
    iHeight = (long)(iWidth * ratio);
}
Share:
42,825
LunchMarble
Author by

LunchMarble

Updated on August 01, 2020

Comments

  • LunchMarble
    LunchMarble almost 4 years

    I am using OpenXML to insert an image into my document. The code provided by Microsoft works, but makes the image much smaller:

    public static void InsertAPicture(string document, string fileName)
            {
                using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(document, true))
                {
                    MainDocumentPart mainPart = wordprocessingDocument.MainDocumentPart;
    
                    ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Jpeg);
    
                    using (FileStream stream = new FileStream(fileName, FileMode.Open))
                    {
                        imagePart.FeedData(stream);
                    }
    
                    AddImageToBody(wordprocessingDocument, mainPart.GetIdOfPart(imagePart));
                }
            }
            private static void AddImageToBody(WordprocessingDocument wordDoc, string relationshipId)
            {
                // Define the reference of the image.
                var element =
                     new Drawing(
                         new DW.Inline(
                             new DW.Extent() { Cx = 990000L, Cy = 792000L },
                             new DW.EffectExtent()
                             {
                                 LeftEdge = 0L,
                                 TopEdge = 0L,
                                 RightEdge = 0L,
                                 BottomEdge = 0L
                             },
                             new DW.DocProperties()
                             {
                                 Id = (UInt32Value)1U,
                                 Name = "Picture 1"
                             },
                             new DW.NonVisualGraphicFrameDrawingProperties(
                                 new A.GraphicFrameLocks() { NoChangeAspect = true }),
                             new A.Graphic(
                                 new A.GraphicData(
                                     new PIC.Picture(
                                         new PIC.NonVisualPictureProperties(
                                             new PIC.NonVisualDrawingProperties()
                                             {
                                                 Id = (UInt32Value)0U,
                                                 Name = "New Bitmap Image.jpg"
                                             },
                                             new PIC.NonVisualPictureDrawingProperties()),
                                         new PIC.BlipFill(
                                             new A.Blip(
                                                 new A.BlipExtensionList(
                                                     new A.BlipExtension()
                                                     {
                                                         Uri =
                                                           "{28A0092B-C50C-407E-A947-70E740481C1C}"
                                                     })
                                             )
                                             {
                                                 Embed = relationshipId,
                                                 CompressionState = A.BlipCompressionValues.Print
                                             },
                                             new A.Stretch(
                                                 new A.FillRectangle())),
                                         new PIC.ShapeProperties(
                                             new A.Transform2D(
                                                 new A.Offset() { X = 0L, Y = 0L },
                                                 new A.Extents() { Cx = 990000L, Cy = 792000L }),
                                             new A.PresetGeometry(
                                                 new A.AdjustValueList()
                                             ) { Preset = A.ShapeTypeValues.Rectangle }))
                                 ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
                         )
                         {
                             DistanceFromTop = (UInt32Value)0U,
                             DistanceFromBottom = (UInt32Value)0U,
                             DistanceFromLeft = (UInt32Value)0U,
                             DistanceFromRight = (UInt32Value)0U,
                             EditId = "50D07946"
                         });
    
                // Append the reference to body, the element should be in a Run.
                wordDoc.MainDocumentPart.Document.Body.AppendChild(new Paragraph(new Run(element)));
            }
    

    I need to make the image its original size. How can I do this? (I have googled how to do this outside of this process, but that is not what I am looking for. I have to assume that there are some sort of size properties inside of the given code).

    Edit: Updated Code (still not working)

    public static void InsertAPicture(string document, string fileName)
    {
        using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(document, true))
        {
            MainDocumentPart mainPart = wordprocessingDocument.MainDocumentPart;
    
            ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Jpeg);
    
            using (FileStream stream = new FileStream(fileName, FileMode.Open))
            {
                imagePart.FeedData(stream);
            }
    
            AddImageToBody(wordprocessingDocument, mainPart.GetIdOfPart(imagePart), fileName);
        }
    }
    private static void AddImageToBody(WordprocessingDocument wordDoc, string relationshipId, string fileName)
    {
    
        var img = new BitmapImage(new Uri(fileName, UriKind.RelativeOrAbsolute));
        var widthPx = img.PixelWidth;
        var heightPx = img.PixelHeight;
        var horzRezDpi = img.DpiX;
        var vertRezDpi = img.DpiY;
        const int emusPerInch = 914400;
        const int emusPerCm = 360000;
        var maxWidthCm = 16.51;
        var widthEmus = (long)(widthPx / horzRezDpi * emusPerInch);
        var heightEmus = (long)(heightPx / vertRezDpi * emusPerInch);
        var maxWidthEmus = (long)(maxWidthCm * emusPerCm);
        if (widthEmus > maxWidthEmus)
        {
            var ratio = (heightEmus * 1.0m) / widthEmus;
            widthEmus = maxWidthEmus;
            heightEmus = (long)(widthEmus * ratio);
        }
    
        // Define the reference of the image.
        var element =
             new Drawing(
                 new DW.Inline(
                     new DW.Extent() { Cx = 990000L, Cy = 792000L },
                     new DW.EffectExtent()
                     {
                         LeftEdge = 0L,
                         TopEdge = 0L,
                         RightEdge = 0L,
                         BottomEdge = 0L
                     },
                     new DW.DocProperties()
                     {
                         Id = (UInt32Value)1U,
                         Name = "Picture 1"
                     },
                     new DW.NonVisualGraphicFrameDrawingProperties(
                         new A.GraphicFrameLocks() { NoChangeAspect = true }),
                     new A.Graphic(
                         new A.GraphicData(
                             new PIC.Picture(
                                 new PIC.NonVisualPictureProperties(
                                     new PIC.NonVisualDrawingProperties()
                                     {
                                         Id = (UInt32Value)0U,
                                         Name = "New Bitmap Image.jpg"
                                     },
                                     new PIC.NonVisualPictureDrawingProperties()),
                                 new PIC.BlipFill(
                                     new A.Blip(
                                         new A.BlipExtensionList(
                                             new A.BlipExtension()
                                             {
                                                 Uri =
                                                   "{28A0092B-C50C-407E-A947-70E740481C1C}"
                                             })
                                     )
                                     {
                                         Embed = relationshipId,
                                         CompressionState = A.BlipCompressionValues.Print
                                     },
                                     new A.Stretch(
                                         new A.FillRectangle())),
                                 new PIC.ShapeProperties(
                                     new A.Transform2D(
                                         new A.Offset() { X = 0L, Y = 0L },
                                         new A.Extents() { Cx = widthEmus, Cy = heightEmus }),
                                     new A.PresetGeometry(
                                         new A.AdjustValueList()
                                     ) { Preset = A.ShapeTypeValues.Rectangle }))
                         ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
                 )
                 {
                     DistanceFromTop = (UInt32Value)0U,
                     DistanceFromBottom = (UInt32Value)0U,
                     DistanceFromLeft = (UInt32Value)0U,
                     DistanceFromRight = (UInt32Value)0U,
                     EditId = "50D07946"
                 });
    
        // Append the reference to body, the element should be in a Run.
        wordDoc.MainDocumentPart.Document.Body.AppendChild(new Paragraph(new Run(element)));
    }
    
  • LunchMarble
    LunchMarble over 12 years
    Is the image dpi constant for both width and height? how do I calculate this?
  • ssarabando
    ssarabando over 12 years
    I treat them as not constant but I have yet to find one where they aren't. If you open the image with the BitmapImage class (System.Windows.Media.Imaging namespace), it has the following properties you can use: PixelWidth, PixelHeight, DpiX, DpiY.
  • LunchMarble
    LunchMarble over 12 years
    I updated my post with the new code. I think I have the code right, but the image size isnt changing.
  • ssarabando
    ssarabando over 12 years
    You didn't set the DW.Extent. Like this: (...) new DW.Extent() { Cx = widthEmus, Cy = heightEmus }, (...)
  • LunchMarble
    LunchMarble over 12 years
    I am fairly sure that is in there, near the bottom. In shape properties?
  • ssarabando
    ssarabando over 12 years
    That's one of the places (A.Extents); the other one is near the top, and it is DW.Extent. 3rd line after var element = ...
  • Styxxy
    Styxxy over 11 years
    Care to add the source where you find this? As it is not your own "finding".
  • apdevelop
    apdevelop over 10 years
    Looks like image file is locked after this call of new BitmapImage(...) I can't delete image file after running that code. How this issue can be solved?
  • Arjan
    Arjan over 7 years
    @ssarabando Do you retrieve the "usable" page width (maxWidthCm) from the document, or is it a magic number?
  • ssarabando
    ssarabando over 7 years
    @Arjan "Magic". In our case is the available width between margins. All our documents generated by this service have a fixed left and right margin witch subtracted to the width of an A4 sheet (in portrait mode) give roughly 15cm (our maxWidthCm). You set it to what's useful for your case.
  • Marius Bidireac
    Marius Bidireac over 5 years
    System.Drawing is available in .Net Core. What is not available is BitmapImage, but you can use Bitmap. See @JCO9's answer.
  • Tomáš Filip
    Tomáš Filip over 3 years
    Notice there is two lines in your code where you have to give the calculated values... This is cruicual part