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

1

9

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).

Depilate answered 19/9, 2015 at 13:3 Comment(1)
I have the same problem and have made some progress by scaling from the extents returned from TextRenderer.MeasureText() to the containing text box. But the text sizes from it aren't as tight around the text as Powerpoint's recalculated scaling. And I got a little further by subtracting leading from the height and pipe character width from the width. More ideas are welcome.Acicular
S
6

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);
    }
Stormystorting answered 29/7, 2016 at 17:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.