OptimizeFonts.cs
//
// This code is part of Document Solutions for PDF demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.IO.Compression;
using System.Drawing;
using System.Collections.Generic;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Common;

namespace DsPdfWeb.Demos
{
    // This sample shows how the GcPdfDocument.OptimizeFonts() method can be used
    // to reduce the size of a PDF by merging duplicate and removing unused embedded fonts.
    //
    // This sample extracts the first five pages of a PDF, and then merges those pages
    // into a single new document. Because each page has its own set of embedded fonts,
    // the created PDF contains redundant font data, resulting in excessive file size.
    // The sample records the merged PDF's size, then it runs OptimizeFonts() on that PDF
    // and records the size of the optimized PDF. These results are printed into the
    // PDF returned by the sample, thus illustrating the effect of running OptimizeFonts().
    //
    // Note that while in this sample the PDF being optimized is somewhat contrived,
    // similar PDFs do appear in real life applications. The OptimizeFonts()
    // method may reduce (sometimes significantly) the size of such PDFs.
    // Actual results will vary.
    // 
    // See also RemoveDuplicateImages.
    public class OptimizeFonts
    {
        public int CreatePDF(Stream stream)
        {
            // Create a 5 page non-optimal PDF:
            var srcFn = Path.Combine("Resources", "PDFs", "CompleteJavaScriptBook.pdf");
            var tmpInput = MakeInputFile(srcFn);
            var fiInput = new FileInfo(tmpInput);

            // Create a new PDF, load the source PDF into it, and optimize it:
            var tmpOutput = Path.GetTempFileName();
            var tmpDoc = new GcPdfDocument();
            using (var fs = File.OpenRead(tmpInput))
            {
                tmpDoc.Load(fs);
                // By default GcPdfDocument uses CompressionLevel.Fastest when saving a PDF.
                // If PDF size is important, we should use optimal compression:
                tmpDoc.CompressionLevel = CompressionLevel.Optimal;
                tmpDoc.OptimizeFonts();
                tmpDoc.Save(tmpOutput);
            }
            var fiOutput = new FileInfo(tmpOutput);

            // Record the input and output file sizes in the resultant PDF:
            var doc = new GcPdfDocument();
            Common.Util.AddNote(String.Format(
                "Using the GcPdfDocument.OptimizeFonts() method reduced the size of a 5-page PDF from {0:N0} to {1:N0} bytes " +
                "by merging duplicate and removing unused font data.\n" +
                "To reproduce these results locally, download and run this sample. You may also modify the sample code to keep the temporary " +
                "input and output files and compare their sizes using a file manager.", fiInput.Length, fiOutput.Length),
                doc.NewPage());
            doc.Save(stream);

            // Clean up:
            File.Delete(tmpInput);
            File.Delete(tmpOutput);

            return doc.Pages.Count;
        }

        static string MakeInputFile(string inFn)
        {
            var indoc = new GcPdfDocument();
            using var fs = File.OpenRead(inFn);
            indoc.Load(fs);

            // Create 5 PDFs from the first 5 pages of the source document:
            var pageCount = 5;
            var docs = new List<GcPdfDocument>(pageCount);
            for (int i = 0; i < pageCount; ++i)
            {
                var outdoc = new GcPdfDocument();
                outdoc.MergeWithDocument(indoc, new MergeDocumentOptions() { PagesRange = new OutputRange(i + 1, i + 1) });
                docs.Add(outdoc);
            }
            // Merge the PDFs into a single document:
            var doc = new GcPdfDocument();
            foreach (var d in docs)
                doc.MergeWithDocument(d);

            // Save the resultant PDF in a temp file:
            var outFn = Path.GetTempFileName();
            doc.Save(outFn);
            return outFn;
        }
    }
}