Font Size Changes after Saving a Presentation and Opening the Same File

Hi,
We are using Aspose.Slides for generating the powerpoint files. We are having an issue when we save the generated file and if we re-open the saved file then the font size is changed. Before saving the file , font size looks correct.

Please review and let us know why it is changing the font after saving the file.

Regrads,
Prathap

@PrathapSV,
Thank you for contacting support.

Please check the problem with the font size using the latest version of Aspose.Slides if it is possible. If the issue persists, please share the following data and information:

  • input and output files
  • code example that reproduces the problem
  • OS version on which the code was executed
  • .NET target platform in your app
  • Aspose.Slides version you used

Hi,

At this moment, we don’t have enough time to re-produce this issue. But we can provide some information which might give you an idea to guide us.

However, we have attached the screenshots “img5” and “img6” for your reference. (img5.png (105.8 KB)
img6.png (91.5 KB)
)
Here
img5.png => This is the PPTX output from Aspose.Slides, which has proper footer text formatting, aquires the entire slide width.

img6.png => This is the same PPTX file saved and re-opened which has different footer text formatting, creates a gap and doesn’t acquire the entire width of the slide.

Note, that all slides in the PPT will have just an image in “.emf”/“metafile” format, added into a picture control during powerpoint creation using aspose slides. Also, please note, if you compare both these image screenshots side by side, you can find almost all of the fonts looks different.

Below is the code for your reference along with other info you asked earlier.
input and output files => we don’t have this at this point, will get it to you soon, but you can refer screenshots attached(img5 n img6.png)
OS version on which the code was executed: Microsoft windows 10 Enterprise LTSC
.NET target platform in your app: 4.8
Aspose.Slides version you used: 21.11.0.0
code example that reproduces the problem: refer below

public bool ConvertToPowerPoint(GrapeCity.ActiveReports.SectionReport sectionReport, string fileName, bool isRootOrSectionHeaderOrDisclaimerBookNode)
    {
        bool success = false;
        try
        {
            this.isRootOrSectionHeaderOrDisclaimerNode = isRootOrSectionHeaderOrDisclaimerBookNode;
            ExportToPowerPointUsingAsposeSlides(sectionReport, fileName);
            success = true;
        }
        catch (Exception ex)
        {
            if (ex is FileNotFoundException)
                ExceptionLog.LogException(ex, "Failed to create PPTX file!");
            else
                throw ex;
        }

        return success;
    }

    private bool ExportToPowerPointUsingAsposeSlides(GrapeCity.ActiveReports.SectionReport activeReportToExport, string fileName)
    {
        bool success = false;
        try
        {
            Presentation pres = GetPresentationObjectFromActiveReport(activeReportToExport);
            if (pres != null)
            {
                pres.Save(fileName, SaveFormat.Pptx);
                success = true;
            }
        }
        catch (Exception ex)
        {
            ExceptionLog.LogException(ex, "Failed to create presentation from active report instance!");
            throw ex;
        }

        return success;
    }

    private Presentation GetPresentationObjectFromActiveReport(GrapeCity.ActiveReports.SectionReport activeReport)
    {
        try
        {
            if (activeReport != null)
            {
                // Never use using() blocks while creating the Presentation object when keeping alive in memory to manipulate later, as it will be disposed.
                Presentation pres = new Presentation();
                SetPresentationSlideSizeAndOrientation(pres, true);

                ISlide firstDefaultSlide = pres.Slides[0];

                for (int i = 0; i < activeReport.Document.Pages.Count; i++)
                {
                    GrapeCity.ActiveReports.Document.Section.Page page = activeReport.Document.Pages[i];
                    string imagePath = GetPageImage(page, i);

                    if (File.Exists(imagePath))
                    {
                        ISlide newSlide = AddSlideAsposeSlides(pres, firstDefaultSlide, imagePath, i + 1);
                        File.Delete(imagePath);
                    }
                }

                pres.Slides.RemoveAt(0);// remove first default slide
                return pres;
            }
        }
        catch (Exception ex)
        {
            ExceptionLog.LogException(ex, "Failed to create Presentation Object From Active Report Instance!");
            throw ex;
        }

        return null;
    }

    private void SetPresentationSlideSizeAndOrientation(Presentation pres, bool isPPTXAttributesDerivedFromActiveReport)
    {
        // VVIMP: Only one size/orientation can be set per presenation, multiple slide sizes across different slides WITHIN a single Presentation is not allowed from Microsoft by design.
        // VVIMP: While setting the Orientation for pptx slide, slide size width and height vaues will be swapped if the Orientation is LandScape, which should happen. 
        // If the Orientation is Portrait then values shouldn't swap. Setting the slide size as per the active report document, instead of default 10" * 7.5" (inches)

        if (PowerPointAttributesObj != null)
        {
            if (isPPTXAttributesDerivedFromActiveReport)
            {
                pres.SlideSize.SetSize(GlobalFunction.ConvertFromInchesToPoints(PowerPointAttributesObj.Size.Width),
                    GlobalFunction.ConvertFromInchesToPoints(PowerPointAttributesObj.Size.Height), SlideSizeScaleType.Maximize);

                pres.SlideSize.Orientation = PowerPointAttributesObj.ActiveReportPageOrientation == GrapeCity.ActiveReports.Document.Section.PageOrientation.Landscape
                                        ? SlideOrientation.Landscape : SlideOrientation.Portrait;
            }
            else
            {
                // Sets SlideSize derived from a presentation document itself.
                pres.SlideSize.SetSize(PowerPointAttributesObj.Size.Width, PowerPointAttributesObj.Size.Height, SlideSizeScaleType.Maximize);
                pres.SlideSize.Orientation = PowerPointAttributesObj.AsposeSlidesPageOrientation;
            }
        }
    }

    private string GetPageImage(GrapeCity.ActiveReports.Document.Section.Page page, int pageIndex)
    {
        string imagePath = string.Concat(Context.Settings.TempFileDir, "pptExport", Context.User.UserId, DateTime.Now.ToString().Replace("/", string.Empty).Replace(":", string.Empty), pageIndex, isRootOrSectionHeaderOrDisclaimerNode ? ".jpeg" : ".emf");
        ExportPageToImage(page, imagePath);
        return imagePath;
    }

    public void ExportPageToImage(GrapeCity.ActiveReports.Document.Section.Page page, string imagePath)
    {
        if (!isRootOrSectionHeaderOrDisclaimerNode)
        {
            Graphics graphics = Graphics.FromHwnd(IntPtr.Zero);
            RectangleF bounds = new RectangleF(0, 0, page.Width * graphics.DpiX, page.Height * graphics.DpiY);
            IntPtr hdc = graphics.GetHdc();

            Metafile metaFile = new Metafile(imagePath, hdc, Rectangle.Round(bounds), MetafileFrameUnit.Pixel);
            Graphics metaFileGraphics = Graphics.FromImage(metaFile);
            RectangleF metaFileBounds = new RectangleF(0, 0, page.Width * metaFileGraphics.DpiX, page.Height * metaFileGraphics.DpiY);

            metaFileGraphics.FillRectangle(Brushes.White, metaFileBounds);
            page.Draw(metaFileGraphics, new RectangleF(PointF.Empty, page.Size));
            metaFileGraphics.Dispose();
            metaFile.Dispose();

            graphics.ReleaseHdc(hdc);
            graphics.Dispose();
        }
        else
        {
            page.Units = GrapeCity.ActiveReports.Document.Section.Units.Pixels;
            Bitmap bmp = new Bitmap((int)page.Width, (int)page.Height);
            Graphics graphics = Graphics.FromImage(bmp);
            graphics.FillRectangle(new SolidBrush(Color.White), 0, 0, bmp.Width, bmp.Height);
            page.Draw(graphics, new RectangleF(0, 0, page.Width, page.Height));
            bmp.Save(imagePath, ImageFormat.Jpeg);
            graphics.Dispose();
            bmp.Dispose();
        }
    }

    private ISlide AddSlideAsposeSlides(Presentation presentation, ISlide asposeFirstDefaultSlide, string imagePath, int slideIndex)
    {
        ISlide asposeSlide = presentation.Slides.InsertClone(slideIndex, asposeFirstDefaultSlide);

        float slideWidth = presentation.SlideSize.Size.Width;
        float slideHeight = presentation.SlideSize.Size.Height;
        byte[] bufferData = File.ReadAllBytes(imagePath);
        IPPImage imgx = presentation.Images.AddImage(bufferData);

        if (this.ImgeSizingType == ImgeSizingMethod.DefaultSlideSize)
        {
            float left = 0;
            float top = 0;
            asposeSlide.Shapes.AddPictureFrame(ShapeType.Rectangle, left, top, slideWidth, slideHeight, imgx);
            //asposeSlide.Shapes[1].LockAspectRatio = MsoTriState.msoTrue;
        }
        else if (this.ImgeSizingType == ImgeSizingMethod.CustomImageSize)
        {
            SizeF finalSize = GetMaxProportionalImageSize(slideWidth, slideHeight, imgx.Width, imgx.Height);
            PointF finalPosition = GetImageCenterPosition(slideWidth, slideHeight, finalSize);
            asposeSlide.Shapes.AddPictureFrame(ShapeType.Rectangle, finalPosition.X, finalPosition.Y, finalSize.Width, finalSize.Height, imgx);
        }

        return asposeSlide;
    }

Note: In the above code sample you can replace GetPageImage() function with any of the metafile image that you have in your machine. In our project, it will be provided by ActiveReports.

This issue is in production and it is very time sensitive as we need to fix as soon as we can, please let us know, how we can resolve this issue.

Also,

  1. if it’s an issue with font, is there a way to embed fonts into PPTX file?
  2. We were using metafile format as it gives better picture quality, is there any other format that we can use which can match this without creating these font issues?

Please review and let us know as soon as you can.

Thanks,
Prathap

@PrathapSV

Thanks for the images and sample code.

It seems that the problem is with the fonts used in the metafiles. But we can say exactly if we get an example of a presentation.

Answers to your questions:

  1. Information on how to embed fonts in a presentation can be found in our documentation
  2. You can try to use SVG images as an alternative to metafiles.

Thank you for the quick response.

We have attached the presentation here. Please check the 2nd slide where footer text formatting is not correct.
0-psv-2482-PPTX-Footer-Issue-Test-New Fact Sheet Layout-v5@160.zip (131.8 KB)

Also, we found that footer text is using “Montserrat” 8 size font which is installed in our server, thus we don’t see this issue when this file is opened in our server. But in my local machine, this font is not installed thus we could see this issue when the same file is opened in my local machine.

So, now by knowing this is font issue, We can think of below options.

  1. I believe font embedding is one option but we think it might increase the file size.
  2. Changing the emf image format to svg or something else, which may not affect the image quality or depend on the font on viewers machine.

Could you please suggest some solutions apart from the above to resolve this issue or should we go with the above options.

One more thing we observed that the footer font info in PowerPoint looks same upon editing that image in PowerPoint but the size shows 7 instead of 8, .Font might be already getting embedded in the file itself, but somehow size is not maintained. Could you please investigate more about this behavior.

Please review and let us know.

Thanks,
Prathap

@PrathapSV
Thank you for the sample presentation.

As far as I understand, the problem is not in our product, but in how the PowerPoint displays metafiles (replaces the fonts used in metafiles if these fonts are not available in the local environment).

So, as a possible solution, you can try to change the font property for the GrapeCity.ActiveReports.Document.Section.Page object:
https://www.grapecity.com/activereportsnet/docs/v14/online/GrapeCity.ActiveReports.Document~GrapeCity.ActiveReports.Document.Section.Page~Font.html

Just use some popular font like Arial instead of Montserrat.

Yes, you are right. The metafile image that we are adding into the slide is depending upon the external fonts, which is creating this issue.

Is there a way that we can convert this metafile image into some other reliable format using Aspose.Slides library to get rid of the dependency with fonts which rely on end user’s machine’s font collection and just display those characters as a symbol, without compromising on quality of the image.

Regards,
Prathap

@PrathapSV

To get rid of the dependency on fonts that rely on the end user’s machine’s font collection you can embed the required font into the presentation (lead to increasing presentation size) or use bitmaps instead of metafiles (leads to compromising image quality).

I tried embedding this font into the document, but it didn’t work, below is the code, can you please check?

Also, mainPres.FontsManager.GetFonts()=> This couldn’t find “Montserrat” font as “.emf” file is referencing this font. So, adding this manually later but still when i opened this file in other machines, it won’t get the embedded font Please let us know are we doing it correctly or not?

public bool EmbedFontsToPowerPoint(string mainOutputFile)
    {
        bool isSuccess = false;
        try
        {
            using (Presentation mainPres = new Presentation(mainOutputFile))
            {
                IFontData[] allFontsUsedInPres = mainPres.FontsManager.GetFonts();// This couldn't find "Montserrat" font as ".emf" file is referencing this font. So, adding manually later
                IFontData[] embeddedFonts = mainPres.FontsManager.GetEmbeddedFonts();
                foreach (IFontData font in allFontsUsedInPres)
                {
                    if (!embeddedFonts.Contains(font))
                        mainPres.FontsManager.AddEmbeddedFont(font, EmbedFontCharacters.OnlyUsed);
                }

                IFontData fontMonsterrat = new FontData("Montserrat");// Embedding "Montserrat" font manually, but still having the issue
                mainPres.FontsManager.AddEmbeddedFont(fontMonsterrat, EmbedFontCharacters.All);
                mainPres.Save(mainOutputFile, SaveFormat.Pptx);
                isSuccess = true;
            }
        }
        catch (Exception ex)
        {
            ExceptionLog.LogException(ex, "Failed to Embed Fonts to PowerPoint");
        }

        return isSuccess;
    }

Thanks,
Prathap

@PrathapSV

Please let me check it. I’ll be back later with the results.

Hi,

Do you have any update on this issue? We are planning to do an hotfix release in a couple of days, please let us know as soon as you can.

Thanks,
Prathap

@PrathapSV

I’ve added a ticket with ID SLIDESNET-43492 to our issue tracking system. We apologize for any inconvenience. Our development team will investigate the case. You will be notified when the issue is resolved.

Thank you for the update.

Any idea how much time it may take to resolve the issue? If it takes longer time, can you suggest any work-around for now (if any)?

Thanks,
Prathap

@PrathapSV
I have requested ETA from the development team.
As I already said, as a possible solution, you can try to change the font property for the GrapeCity.ActiveReports.Document.Section.Page object when generating EMF file:
https://www.grapecity.com/activereportsnet/docs/v14/online/GrapeCity.ActiveReports.Document~GrapeCity.ActiveReports.Document.Section.Page~Font.html

Thanks for the info.

Unfortunately, we can’t change Font property of GrapeCity.ActiveReports.Document.Section.Page object, as it defeats the purpose of giving font selection option while designing the report for end users:
https://www.grapecity.com/activereportsnet/docs/v14/online/GrapeCity.ActiveReports.Document~GrapeCity.ActiveReports.Document.Section.Page~Font.html

Also, we have been trying following options to resolve this issue if possible.

  1. If there is a way to embed the fonts into the “MetaFile” image object itself when we create the “MetaFile” object in our code? Not sure whether it is possible or not.

OR

  1. From the current behavior we can conclude that “MetaFile” image in PowerPoint can render the fonts, if available in windows, but if we embed the same fonts at “PowerPoint” application level, they cannot access/ignores it. If there is a way to direct “MetaFile” to look inside the “PowerPoint” instead of “Windows” for fonts, while creating “MetaFile”/“PowerPoint” object. Hope it might resolve the issue.

We know, some of these questions may not be related to Aspose.Slides 100%, but we think we might get benefitted from you or your team expertise on these above approaches, as we are very tight on our schedule and planning to release this fix in our next hotfix which is due in next 2 days.

Please let us know.

Regards,
Prathap

@PrathapSV
As far as I remember, metafiles can’t store font data (they only store basic data such as font facename, size, style and etc). Please see EMF specification.

This leaves us with only the second option. Our team is already investigating this case and I will let you know as soon as I have the results. Thank you for your patience.

Any update on this issue?

Also, could you please let us know the ETA for this?

Thanks,
Prathap

@PrathapSV,
Our developers have investigated the case. Unfortunately, we are not able to embed the font in the metafile because its specification does not support such a possibility. Also, we have no way to affect the operation of PowerPoint, where metafile rendering is based on the fonts installed in the system and does not accept fonts embedded in the presentation. However, we can offer a workaround by resaving the image represented by the metafile to a bitmap format that does not use external fonts. Of course, the rendering quality of such an image will be lower than the original metafile, but acceptable quality can be achieved by increasing the size of the bitmap using the scaling factor, choosing it to be such that it will display the image with improved quality. Below is an example of replacing a vector image with a raster image with an increase in its size:

public void EmbedFontsToPowerPoint(string mainOutputFile)
{
    using (Presentation mainPres = new Presentation(mainOutputFile))
    {
        // Get the picture frame that represents the source metafile
        PictureFrame pFrame = mainPres.Slides[1].Shapes[0] as PictureFrame;

        // Declare a scaling factor to improve the image quality
        float scalrFactor = 2f;

        // Get a bitmap from the source metafile
        Image img = pFrame.PictureFormat.Picture.Image.SystemImage.GetThumbnailImage((int)(pFrame.Width * scalrFactor), (int)(pFrame.Height * scalrFactor), null, new IntPtr());

        // Add the bitmap to the image collection
        IPPImage image = mainPres.Images.AddImage(img);

        // Create a new picture frame for the bitmap
        mainPres.Slides[1].Shapes.AddPictureFrame(ShapeType.Rectangle, pFrame.X, pFrame.Y, pFrame.Width, pFrame.Height, image);

        // Remove the picture frame that represents the source metafile
        mainPres.Slides[1].Shapes.Remove(pFrame);

        // Save the presentation
        mainPres.Save(mainOutputFile, SaveFormat.Pptx);
    }
}

Documents: Image, Picture Frame
API Reference: PictureFrame class