1

Ok I have a problem that just baffles me yet again. I have some code that turns a DOC file to a PNG file. When I do it on a localhost, the image is fine. When I take the same code and put it on live server, the image is extremely small (same size as the DOT file what I got the DOC file from, basically DOT gets filled out and turned into a DOC). Now... here's the crazy part. If I log into the hosting server as an admin and THEN go to the live website, the image is large and crisp, even if I go to the site from an iPhone. As soon as I log out of the hosting server and refresh the live page, image is tiny again. Here's the code I am using to convert DOC to PNG. On a side note, if I use method 2, I can make the image bigger and higher resolution, but fonts are out of place.

    private void ConvertDocToPNG(string startupPath, string filename1)
    {
        var docPath = Path.Combine(startupPath, filename1);
        Application app = new Application();
        Microsoft.Office.Interop.Word.Document doc = new Microsoft.Office.Interop.Word.Document();
        app.Visible = false;
        doc = app.Documents.Open(docPath);
        app.WindowState = Microsoft.Office.Interop.Word.WdWindowState.wdWindowStateMaximize;
        app.ActiveWindow.ActivePane.View.Zoom.Percentage = 100;
        doc.ShowGrammaticalErrors = false;
        doc.ShowRevisions = false;
        doc.ShowSpellingErrors = false;

        //Opens the word document and fetch each page and converts to image
        foreach (Microsoft.Office.Interop.Word.Window window in doc.Windows)
        {
            foreach (Microsoft.Office.Interop.Word.Pane pane in window.Panes)
            {
                for (var i = 1; i <= pane.Pages.Count; i++)
                {
                    Microsoft.Office.Interop.Word.Page page = null;
                    bool populated = false;
                    while (!populated)
                    {
                        try
                        {
                            // This !@#$ variable won't always be ready to spill its pages. If you step through
                            // the code, it will always work.  If you just execute it, it will crash.  So what
                            // I am doing is letting the code catch up a little by letting the thread sleep
                            // for a microsecond.  The second time around, this variable should populate ok.
                            page = pane.Pages[i];
                            populated = true;
                        }
                        catch (COMException ex)
                        {
                            Thread.Sleep(1);
                        }
                    }
                    var bits = page.EnhMetaFileBits;
                    var target = Path.Combine(startupPath + "\\", string.Format("{1}_page_{0}", i, filename1.Split('.')[0]));

                    try
                    {
                        using (var ms = new MemoryStream((byte[])(bits)))
                        {
                            var image = System.Drawing.Image.FromStream(ms);
                            var pngTarget = Path.ChangeExtension(target, "png");

                            // Method 2
                            image.Save(pngTarget, System.Drawing.Imaging.ImageFormat.Png);

                            // Another way to save it using custom size
                            //float width = Convert.ToInt32(hfIdCardMaxWidth.Value);
                            //float height = Convert.ToInt32(hfIdCardMaxHeight.Value);
                            //float scale = Math.Min(width / image.Width, height / image.Height);
                            //int resizedWidth = (int)Math.Round(image.Width * scale);
                            //int resizedHeight = (int)Math.Round(image.Height * scale);
                            //Bitmap myBitmap = new Bitmap(image, new Size(resizedWidth, resizedHeight));
                            //myBitmap.Save(pngTarget, System.Drawing.Imaging.ImageFormat.Png);
                        }
                    }
                    catch (System.Exception ex)
                    {
                        doc.Close(true, Type.Missing, Type.Missing);
                        Marshal.ReleaseComObject(doc);
                        doc = null;
                        app.Quit(true, Type.Missing, Type.Missing);
                        Marshal.ReleaseComObject(app);
                        app = null;
                        throw ex;
                    }
                }
            }
        }
        doc.Close(true, Type.Missing, Type.Missing);
        Marshal.ReleaseComObject(doc);
        doc = null;
        app.Quit(true, Type.Missing, Type.Missing);
        Marshal.ReleaseComObject(app);
        app = null;
    }
Lukas
  • 2,885
  • 2
  • 29
  • 31
  • Is it safe to assume you want this code to run in an unattended fashion? Using the Word interop (or any MS office interop) in this way is a bad idea, but we might be able to make something work for you with the OpenXML SDK. If this code is being run in an interactive fashion, then disregard this comment :) – Bill Sambrone Jul 25 '14 at 20:08
  • Unattended fashion indeed. – Lukas Jul 25 '14 at 20:11

2 Answers2

1

Given that you are using the interop in an unattended fashion, all sorts of weird/unexpected things can happen. I will admit, I don't know why you are experiencing the symptoms you are given your test cases in different environments. I have a very strong feeling being unattended is the culprit. The interop runs in the user's login context, and if there is no user... well... yeah. So, how to get around this and still be unattended? The first that comes to mind is using the OpenXML SDK. This is the safe way of manipulating office documents in an unattended fashion. I use it for unattended report generation myself.

Assumptions:

  • Standard DOCX format
  • The doc contains words/pictures/styles/whatever. It's not just a sack of images (if it is, there are much easier ways to accomplish what you need)

The API:

http://www.microsoft.com/en-us/download/details.aspx?id=30425

But, you can't convert a doc to an image with OpenXML! I thought of a workaround, but this is NOT tested. The idea is to convert the doc to html, then render out the html and stuff it into an image.

Here is a way to convert your word doc to HTML using OpenXML:

The big set of power tools that can do all sorts of handy things: http://powertools.codeplex.com/

The specific module that you will need: http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2014/01/30/transform-docx-to-html-css-with-high-fidelity-using-powertools-for-open-xml.aspx

Here is a handy library to render out the HTML and dump it into an image: http://htmlrenderer.codeplex.com/

Bill Sambrone
  • 4,334
  • 4
  • 48
  • 70
  • It's a bit of an overkill since all I really need to do to increase the image resolution and get a viable image out of the whole thing is convert MemoryStream into a byte array, then into a Bitmap, from where I can resize it as needed. Problem is, when you open the document, at 100% zoom, it already is extremely tiny, so in a sense, the code is working as intended. I am just baffled that logging in gives me far better view (2023x1279 vs something like 200x120). Nonetheless, I will give you an up vote for the well written reply :) even though it's not exactly what I was after. – Lukas Jul 25 '14 at 23:15
  • Thanks! I agree its overkill, but I've ran into so many landmines with using the interop in an unattended windows service that I'm pretty scared of it. – Bill Sambrone Jul 25 '14 at 23:16
0
using (var ms = new MemoryStream((byte[])(bits)))
        {
            var emf = new Metafile(ms);
            var scale = 400 / emf.HorizontalResolution;
            var Width= emf.Width * scale;
            var Height = emf.Height * scale;
            System.Drawing.Bitmap b = new System.Drawing.Bitmap((Int32)Width, (Int32)Height);
            var G = System.Drawing.Graphics.FromImage(b);
            G.Clear(System.Drawing.Color.White);
            G.DrawImage(emf, 0, 0, (float)Width, (float)Height);
            b.Save(pngTarget, System.Drawing.Imaging.ImageFormat.Png);
        }
andrew
  • 1