Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate the size (Extents) of a text (TextBody) in OpenXML (PresentationML) PowerPoint (PPTX)

I want to create a presentation with data coming from a database. I manage to get valid presentations that open in PowerPoint (the Open XML Productivity Tool of the SDK 2.5 is a big help in doing this). But, how to calculate the size of the textbox shapes? I see where to put the values, but new Extents() defaults to zero width and height. When I take some values of a Shape from an existing presentation, I may get the correct height (at least for texts with one line or a fixed number of lines), but the text will overflow to the right or at the bottom (depending on wordwrap setting).

Also inserting NormalAutoFit in the BodyProperties of the TextBody doesn't help - the necessary values for FontScale and LineSpaceReduction aren't calculated in OpenXML.

So, what's best practice for setting the Extents of a Shape with a TextBody?

Is there a built-in way to calculate the Extents of a given TextBody or Shape? (some rule of thumb with built-in methods would be better than nothing)

I know that PowerPoint will recalculate the values of NormalAutoFit after any change was made (at least for a bunch of slides before and after that change), but this doesn't help when the presentation is started before a change was made (or if it was started with the PowerPoint viewer).

like image 556
outofmind Avatar asked Sep 19 '15 13:09

outofmind


1 Answers

From Eric White's Forum

This is a non-trivial (but doable) task.

After many experiments, I found that the text metrics methods in System.Windows.Forms.TextRenderer gave me the best results. This is the text metric functionality that WmlToHtmlConverter uses. You can look at the code in WmlToHtmlConverter as one example of the use of TextRenderer.

Here's the code I've come up with for my purposes based on Eric White's WmlToHtmlConverter, this post and this. I use this to calculate the dimensions of TextBox for text watermarks and for image watermarks for OpenXml for Word documents.

    private static D.Size pixelsToEmus(int widthPx, int heightPx, double resDpiX, double resDpiY, int zoomX, int zoomY)
    {
        const int emusPerInch = 914400;
        const int emusPerCm = 360000;
        const decimal maxWidthCm = 16.51m;
        var widthEmus = (int)(widthPx / resDpiX * emusPerInch) * zoomX / 100;
        var heightEmus = (int)(heightPx / resDpiY * emusPerInch) * zoomY / 100;
        var maxWidthEmus = (int)(maxWidthCm * emusPerCm);
        if (widthEmus > maxWidthEmus)
        {
            var ratio = ((decimal)heightEmus / (decimal)widthEmus);
            widthEmus = maxWidthEmus;
            heightEmus = (int)(widthEmus * ratio);
        }
        return new D.Size(widthEmus, heightEmus);
    }

    public static D.Size GetTextSize(this CWatermarkItemBase watermark, string runText)
    {
        var fs = watermark.GetFontStyle();
        var sz = watermark.FontSize;
        var proposedSize = new D.Size(int.MaxValue, int.MaxValue);
        D.Size sf;

        using (var ff = new D.FontFamily(watermark.FontFamily))
        {
            try
            {
                using (var f = new D.Font(ff, (float)sz, fs))
                {
                    const TextFormatFlags tff = TextFormatFlags.NoPadding;
                    sf = TextRenderer.MeasureText(runText, f, proposedSize, tff);
                }
            }
            catch (ArgumentException)
            {
                try
                {
                    const D.FontStyle fs2 = D.FontStyle.Regular;
                    using (D.Font f = new D.Font(ff, (float)sz, fs2))
                    {
                        const TextFormatFlags tff = TextFormatFlags.NoPadding;
                        sf = TextRenderer.MeasureText(runText, f, proposedSize, tff);
                    }
                }
                catch (ArgumentException)
                {
                    const D.FontStyle fs2 = D.FontStyle.Bold;
                    try
                    {
                        using (var f = new D.Font(ff, (float)sz, fs2))
                        {
                            const TextFormatFlags tff = TextFormatFlags.NoPadding;
                            sf = TextRenderer.MeasureText(runText, f, proposedSize, tff);
                        }
                    }
                    catch (ArgumentException)
                    {
                        // if both regular and bold fail, then get metrics for Times New Roman
                        // use the original FontStyle (in fs)
                        using (var ff2 = new D.FontFamily("Times New Roman"))
                        using (var f = new D.Font(ff2, (float)sz, fs))
                        {
                            const TextFormatFlags tff = TextFormatFlags.NoPadding;
                            sf = TextRenderer.MeasureText(runText, f, proposedSize, tff);
                        }
                    }
                }
            }
        }
        D.Size s2 = pixelsToEmus(sf.Width, sf.Height, 96, 96, 100, 100);
        return s2;
    }
    public static D.Size GetImageSize(this CWatermarkItemImage watermarkItem)
    {
        var img = new BitmapImage(new Uri(watermarkItem.FilePath, UriKind.RelativeOrAbsolute));
        return pixelsToEmus(img.PixelWidth, img.PixelHeight, img.DpiX, img.DpiY, watermarkItem.ZoomWidth, watermarkItem.ZoomHeight);
    }
like image 91
tdemay Avatar answered Nov 09 '22 06:11

tdemay