• Creating a Word Document with the Open XML SDK 2.0

    9
     
    February 25th, 2009stuartwhiteford.NET, C#

    Introduction

    I’m always eager to find ways of making my life easier, so recently I’ve been searching for a method of creating a Microsoft Word document using purely managed code (none of that Object Model awfulness). I’d been playing around with version 1.0 of the Open XML SDK and while this works fine, it’s not strongly typed, so requires you to manipulate the XML directly.

    Brian Jones has an excellent post about the SDK in his blog: – http://blogs.msdn.com/brian_jones/archive/2008/10/06/open-xml-format-sdk-2-0.aspx, which also includes an example of how to create a basic Word document in C#. This post hopes to demonstrate how to create a document that includes a header and an image.

    If you’re following along with this, you’ll need the SDK if you don’t have it already, you can get it from: – http://go.microsoft.com/fwlink/?LinkId=127912.

    Creating the Console Application

    Open up Visual Studio and create a new Console Application. In this demo I’ve called it DocumentBuilder. Next add a reference to the DocumentFormat.OpenXML and WindowsBase dlls.

    In this example, to include the image, I’ve stored it as a Resource, so add a new Resources File to your project (I’ve called it DocumentResources.resx) and add your image, the image should get copied into a new Resources folder in your project.

    Add these using statements to the Program.cs file: -

    using System.IO;
    using DocumentFormat.OpenXml;
    using DocumentFormat.OpenXml.Packaging;
    using DocumentFormat.OpenXml.Wordprocessing;
    using d = DocumentFormat.OpenXml.Drawing;

    Now add the following constants to the Program class: -

    private const double EMU_PER_PIXEL = 9525;
    private const string GRAPHIC_DATA_URI = @"http://schemas.openxmlformats.org/drawingml/2006/picture";

    To keep a bit of structure about the program I’ve implemented three methods, one to build the document itself, one to build the header and finally one to insert the image. Firstly, add the following code for the BuildDocument method: -

    private static void BuildDocument(string fileName)
    {
        using (WordprocessingDocument w = WordprocessingDocument.Create(fileName, WordprocessingDocumentType.Document))
        {
            MainDocumentPart mp = w.AddMainDocumentPart();
            Document d = new Document();
            Body b = new Body();
            Paragraph p = new Paragraph();
            Run r = new Run();
            Text t = new Text();
            t.Text = "This is some body text.";
            r.Append(t);
            p.Append(r);
            b.Append(p);
            HeaderPart hp = mp.AddNewPart<HeaderPart>();
            string headerRelationshipID = mp.GetIdOfPart(hp);
            SectionProperties sectPr = new SectionProperties();
            HeaderReference headerReference= newHeaderReference();
            headerReference.Id = headerRelationshipID;
            headerReference.Type = HeaderFooterValues.Default;
            sectPr.Append(headerReference);
            b.Append(sectPr);
            d.Append(b);
            hp.Header = BuildHeader(hp, "This is some header text.");
            hp.Header.Save();
            mp.Document = d;
            mp.Document.Save();
            w.Close();
        }
    }

    Next, add the following for the BuildHeader method: -

    private static Header BuildHeader(HeaderPart hp, string title)
    {
        // Add an ImagePart.
        ImagePart ip = hp.AddImagePart(ImagePartType.Jpeg);
        string imageRelationshipID = hp.GetIdOfPart(ip);
        using (Stream imgStream = ip.GetStream())
        {
            System.Drawing.Bitmap logo = DocumentResources.sw;
            logo.Save(imgStream, System.Drawing.Imaging.ImageFormat.Jpeg);
        }
        Header h = new Header();
        Paragraph p = new Paragraph();
        Run r = new Run();
        Drawing drawing = BuildImage(imageRelationshipID, "sw.gif", 48, 48);
        r.Append(drawing);
        p.Append(r);
        r = new Run();
        RunProperties rPr = new RunProperties();
        TabChar tab = new TabChar();
        Bold b = new Bold();
        Color color = new Color { Val = "006699" };
        FontSize sz = new FontSize { Val = 40 };
        Text t = new Text { Text = title };
        rPr.Append(b);
        rPr.Append(color);
        rPr.Append(sz);
        r.Append(rPr);
        r.Append(tab);
        r.Append(t);
        p.Append(r);
        h.Append(p);
        return h;
    }

    Lastly, add the BuildImage method: -

    private static Drawing BuildImage(string imageRelationshipID, string imageName,
        int pixelWidth, int pixelHeight)
    {
        int emuWidth = (int)(pixelWidth * EMU_PER_PIXEL);
        int emuHeight = (int)(pixelHeight * EMU_PER_PIXEL);
        Drawing drawing = new Drawing();
        d.Wordprocessing.Inline inline = new d.Wordprocessing.Inline { DistanceFromTop = 0, DistanceFromBottom = 0, DistanceFromLeft = 0, DistanceFromRight = 0 };
        d.Wordprocessing.Anchor anchor = new d.Wordprocessing.Anchor();
        d.Wordprocessing.SimplePosition simplePos = new d.Wordprocessing.SimplePosition { X = 0, Y = 0 };
        d.Wordprocessing.Extent extent = new d.Wordprocessing.Extent { Cx = emuWidth, Cy = emuHeight };
        d.Wordprocessing.DocProperties docPr = new d.Wordprocessing.DocProperties { Id = 1, Name = imageName };
        d.Graphic graphic = new d.Graphic();
        // We don’t have to hard code a URI anywhere else in the document but if we don’t do it here 
        // we end up with a corrupt document.
        d.GraphicData graphicData = new d.GraphicData { Uri = GRAPHIC_DATA_URI };
        d.Pictures.Picture pic = new d.Pictures.Picture();
        d.Pictures.NonVisualPictureProperties nvPicPr = new d.Pictures.NonVisualPictureProperties();
        d.Pictures.NonVisualDrawingProperties cNvPr = new d.Pictures.NonVisualDrawingProperties { Id = 2, Name = imageName };
        d.Pictures.NonVisualPictureDrawingProperties cNvPicPr = new d.Pictures.NonVisualPictureDrawingProperties();
        d.Pictures.BlipFill blipFill = new d.Pictures.BlipFill();
        d.Blip blip = new d.Blip { Embed = imageRelationshipID };
        d.Stretch stretch = new d.Stretch();
        d.FillRectangle fillRect = new d.FillRectangle();
        d.Pictures.ShapeProperties spPr = new d.Pictures.ShapeProperties();
        d.Transform2D xfrm = new d.Transform2D();
        d.Offset off = new d.Offset { X = 0, Y = 0 };
        d.Extents ext = new d.Extents { Cx = emuWidth, Cy = emuHeight };
        d.PresetGeometry prstGeom = new d.PresetGeometry { Preset = d.ShapeTypeValues.Rectangle };
        d.AdjustValueList avLst = new d.AdjustValueList();
        xfrm.Append(off);
        xfrm.Append(ext);
        prstGeom.Append(avLst);
        stretch.Append(fillRect);
        spPr.Append(xfrm);
        spPr.Append(prstGeom);
        blipFill.Append(blip);
        blipFill.Append(stretch);
        nvPicPr.Append(cNvPr);
        nvPicPr.Append(cNvPicPr);
        pic.Append(nvPicPr);
        pic.Append(blipFill);
        pic.Append(spPr);
        graphicData.Append(pic);
        graphic.Append(graphicData);
        inline.Append(extent);
        inline.Append(docPr);
        inline.Append(graphic);
        drawing.Append(inline);
        return drawing;
    }

    All that remains is to call the BuildDocument from the Main method: -

    public static void Main(string[] args)
    {
        BuildDocument(@"C:\TempTest.docx");
    }

    Press F5 to run your app and you should get your Word document. If you’re using your own image in the header you’ll notice that it’s a bit mis-shapen (unless you happen to have picked a 48 x 48 pixel image). To correct this just change the pixelHeight and pixelWidth parameters passed into the BuildImage method to suit.

    References

    Tags: , , ,
 

8 responses to “Creating a Word Document with the Open XML SDK 2.0” RSS icon

  • Hi,
    this fails the document could not be opened due to problems with document.xml line 1 column 336 – referring to, I think,

    I had to comment out these lines
    // System.Drawing.Bitmap logo = DocumentResources.sw;
    // logo.Save(imgStream, System.Drawing.Imaging.ImageFormat.Jpeg);

    because I have no idea what Reference is needed to resolve the “DocumentResources” class. Can you say which it is?

    Any ideas?
    I’m using .NET 3.5 / visual studio 2008 – but yearn to return to Java :)

  • stuartwhiteford

    Hi James,

    In Visual Studio, right click the project, add a new item, select Resources File (my one is called DocumentResources.resx). Visual Studio should open the newly created file for you. Click Add Resource and select Add Existing File… then select the image you want to put in your header.

    In the Program.cs file add a using statement for System.Resources if it’s not already there.

    Stuart.

  • Sucha Sudarsanam

    Hi Stuart,

    What assembly is “Drawing” in?

    Thanks,
    Sucha

  • Hi Sucha,

    The Drawing class used in the BuildImage method is in the DocumentFormat.OpenXml.Wordprocessing namespace which in turn is in the DocumentFormat.OpenXml assembly.

    Stuart.

  • Hi stuart,

    U have done the gr8 work…
    it resolves my task.

    but the issue now is… I want to add two iamges in Header and for 1st image the allignment sld be Left and for other image the allignment sld be Left.

    How can we handle this?

  • Hi,

    great tutorial but I do have a problem :-)
    It say’s :’ShapeTypeValues’ is not a member of ‘DocumentFormat.OpenXml.Wordprocessing.Drawing’

    Any suggestions?

    Thanks!

  • Great Article.I am able to create the Header. I want to Keep Image position on right.

    Can anybody suggest how to do this using this code?

  • uriel fernandez

    This was just awesome!

    I used your code to generate the package word document on an ASP.NET application.

    great stuff. It would’ve taken me months to figure all this our.

    -Uriel


1 Trackbacks / Pingbacks

Leave a reply