Combinar varios documentos en un solo Documento PDF en .NET

Categoría del blog: PDF.NET

25.11.2020

Este artículo contiene código en C# que permite combinar varios documentos de entrada (PDF, TIFF, PNG, DOCX, etc.) en un único documento PDF. Cada imagen de entrada se añade como una página PDF de solo imagen, y cada página del documento (PDF, DOCX) se convierte en una página PDF. El documento PDF creado contiene un marcador para cada página PDF creada. Además, el documento PDF incluye una página de contenido con marcadores interactivos para todas las páginas PDF.

Aquí se muestra código en C# que permite combinar varios documentos de entrada (PDF, TIFF, PNG, DOCX, etc.) en un único documento PDF:
/// <summary>
/// Merges several documents (PDF, TIFF, PNG, DOCX, ...) to single PDF document.
/// </summary>
/// <param name="outputFilename">The output PDF filename.</param>
/// <param name="inputFilenames">The filenames of input documents.</param>
public static void MergeDocuments(string outputFilename, params string[] inputFilenames)
{
    if (inputFilenames == null || inputFilenames.Length < 1)
        throw new System.ArgumentOutOfRangeException("inputFilenames");

    // define style of index page
    string indexPageFontName = "Arial";
    float indexPageFontSize = 14;
    System.Drawing.PointF indexPagePadding = new System.Drawing.PointF(indexPageFontSize * 2, indexPageFontSize * 4);
    float indexPageLineSpacing = indexPageFontSize * 0.8f;
    System.Drawing.Color linkFontColor = System.Drawing.Color.Blue;
    System.Drawing.Color titleFontColor = System.Drawing.Color.Black;

    // create output PDF document
    using (Vintasoft.Imaging.Pdf.PdfDocument document = 
        new Vintasoft.Imaging.Pdf.PdfDocument(outputFilename, Vintasoft.Imaging.Pdf.PdfFormat.Pdf_17))
    {
        // create bookmark collection
        document.Bookmarks = new Vintasoft.Imaging.Pdf.Tree.PdfBookmarkCollection(document);

        // create index page
        Vintasoft.Imaging.Pdf.Tree.PdfPage indexPage =
            new Vintasoft.Imaging.Pdf.Tree.PdfPage(document, Vintasoft.Imaging.PaperSizeKind.A4);
        // add index page to PDF document
        document.Pages.Add(indexPage);
        // create annotation collection for index page
        indexPage.Annotations = new Vintasoft.Imaging.Pdf.Tree.Annotations.PdfAnnotationList(document);

        // create font and text brush
        Vintasoft.Imaging.Pdf.Tree.Fonts.PdfFont font;
        using (System.IO.Stream fontProgramStream = document.FontProgramsController.GetTrueTypeFontProgram(indexPageFontName))
            font = document.FontManager.CreateCIDFontFromTrueTypeFont(fontProgramStream);
        Vintasoft.Imaging.Pdf.Drawing.PdfBrush fontBrush = 
            new Vintasoft.Imaging.Pdf.Drawing.PdfBrush(linkFontColor);

        // open PdfGraphics on index page
        using (Vintasoft.Imaging.Pdf.Drawing.PdfGraphics indexGraphics = indexPage.GetGraphics())
        {
            // start location in index page
            System.Drawing.PointF indexPageTextLocation = indexPage.MediaBox.Location;
            indexPageTextLocation.X += indexPagePadding.X;
            indexPageTextLocation.Y += indexPage.MediaBox.Height - indexPagePadding.Y - indexPageFontSize * 2;

            // draw index title
            System.Drawing.RectangleF titleRect = new System.Drawing.RectangleF(
                indexPageTextLocation.X,
                indexPageTextLocation.Y,
                indexPage.MediaBox.Width - indexPageTextLocation.X * 2,
                indexPageFontSize * 2 * 2);
            indexGraphics.DrawString(
                "Example: Create PDF document from several documents.",
                font, indexPageFontSize * 2, new Vintasoft.Imaging.Pdf.Drawing.PdfBrush(titleFontColor), 
                titleRect, Vintasoft.Imaging.Pdf.Drawing.PdfContentAlignment.Center, true);

            indexPageTextLocation.Y -= indexPageLineSpacing * 4;

            // location of page number
            float indexPageNumberLocationX = indexPage.MediaBox.X + indexPage.MediaBox.Width - indexPagePadding.X;

            // for each input file
            foreach (string inputFilename in inputFilenames)
            {
                int firstPageIndex = document.Pages.Count;

                // create bookmark
                Vintasoft.Imaging.Pdf.Tree.PdfBookmark fileBookmark = 
                    new Vintasoft.Imaging.Pdf.Tree.PdfBookmark(document, System.IO.Path.GetFileName(inputFilename));
                fileBookmark.Destination = new Vintasoft.Imaging.Pdf.Tree.PdfDestinationFit(document, firstPageIndex);
                document.Bookmarks.Add(fileBookmark);

                // if input image file is PDF document
                if (System.IO.Path.GetExtension(inputFilename).ToUpper() == ".PDF")
                {
                    // open input PDF document
                    using (Vintasoft.Imaging.Pdf.PdfDocument inputDoc = new Vintasoft.Imaging.Pdf.PdfDocument(inputFilename))
                    {
                        // add pages from input PDF document to the output document, also add bookmark tree from input PDF document to the 'fileBookmark'
                        document.AddDocument(inputDoc, fileBookmark.ChildBookmarks);
                    }
                }
                else
                {
                    // add images from input image file to the output document
                    Vintasoft.Imaging.ImageCollection images = new Vintasoft.Imaging.ImageCollection();
                    images.Add(inputFilename);
                    // if input file is vector document (DOCX, ...)
                    if (images[0].IsVectorImage)
                    {
                        // create temp stream
                        using (System.IO.MemoryStream tempStream = new System.IO.MemoryStream())
                        {
                            // create PDF encoder
                            using (Vintasoft.Imaging.Codecs.Encoders.PdfEncoder encoder = new Vintasoft.Imaging.Codecs.Encoders.PdfEncoder())
                            {
                                // convert input file to temporary PDF document
                                images.SaveSync(tempStream, encoder);
                                // add pages from temp PDF document to the output document
                                using (Vintasoft.Imaging.Pdf.PdfDocument tempDoc = new Vintasoft.Imaging.Pdf.PdfDocument(tempStream))
                                    document.AddDocument(tempDoc, fileBookmark.ChildBookmarks);
                            }
                        }
                    }
                    else
                    {
                        // encode input file as image-only PDF pages
                        foreach (Vintasoft.Imaging.VintasoftImage image in images)
                            AddImageToPdfDocument(document, image);
                    }
                    images.ClearAndDisposeItems();
                }

                // if 'fileBookmark' does not have children bookmarks AND added document is multipage document, i.e.
                // input document is PDF document without bookmarks OR input document is NOT PDF document
                if (fileBookmark.ChildBookmarks.Count == 0 && (document.Pages.Count - firstPageIndex) > 1)
                {
                    // for each page in input document
                    for (int i = firstPageIndex; i < document.Pages.Count; i++)
                    {
                        // create bookmark for page
                        Vintasoft.Imaging.Pdf.Tree.PdfBookmark pageBookmark =
                            new Vintasoft.Imaging.Pdf.Tree.PdfBookmark(document, string.Format("Page {0}", i - firstPageIndex + 1));
                        // set destination for page bookmark
                        pageBookmark.Destination = new Vintasoft.Imaging.Pdf.Tree.PdfDestinationFit(document, i);
                        // add page bookmark to the file bookmark
                        fileBookmark.ChildBookmarks.Add(pageBookmark);
                    }
                }

                // draw link text on index page
                System.Drawing.RectangleF indexRect = DrawIndexString(
                    indexGraphics, System.IO.Path.GetFileName(inputFilename), font, indexPageFontSize, 
                    fontBrush, indexPageTextLocation, firstPageIndex + 1, indexPageNumberLocationX);

                // add link annotation on link text
                Vintasoft.Imaging.Pdf.Tree.Annotations.PdfLinkAnnotation linkAnnotation =
                    new Vintasoft.Imaging.Pdf.Tree.Annotations.PdfLinkAnnotation(document.Pages[firstPageIndex], fileBookmark.Destination);
                linkAnnotation.Rectangle = indexRect;
                linkAnnotation.HighlightingMode = Vintasoft.Imaging.Pdf.Tree.Annotations.PdfAnnotationHighlightingMode.Invert;
                indexPage.Annotations.Add(linkAnnotation);

                // move to next link
                indexPageTextLocation.Y -= indexRect.Height + indexPageLineSpacing;
            }
        }
        
        // subset font that is used for text drawing
        document.FontManager.PackFont(font);

        // save chages in the output document
        document.SaveChanges();
    }
}

/// <summary>
/// Draws the index string.
/// </summary>
/// <param name="graphics">The graphics.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="fontSize">Size of the font.</param>
/// <param name="brush">The brush.</param>
/// <param name="textLocation">The text location.</param>
/// <param name="pageNumber">The page number.</param>
/// <param name="pageNumberLocationX">The page number location at X-axis.</param>
/// <returns>Rectangle, where text was drawn.</returns>
private static System.Drawing.RectangleF DrawIndexString(
    Vintasoft.Imaging.Pdf.Drawing.PdfGraphics graphics, 
    string text,
    Vintasoft.Imaging.Pdf.Tree.Fonts.PdfFont font, 
    float fontSize,
    Vintasoft.Imaging.Pdf.Drawing.PdfBrush brush,
    System.Drawing.PointF textLocation, 
    int pageNumber, 
    float pageNumberLocationX)
{
    // draw text
    float width, height;
    graphics.DrawString(text, font, fontSize, brush, textLocation);
    graphics.MeasureString(text, font, fontSize, out width, out height);
    
    // draw dots
    float lineWidth = 1;
    System.Drawing.PointF pageNumberLocation = new System.Drawing.PointF(pageNumberLocationX, textLocation.Y);
    using (Vintasoft.Imaging.Pdf.Drawing.PdfPen pen = new Vintasoft.Imaging.Pdf.Drawing.PdfPen(brush.Color, lineWidth))
    {
        pen.DashPattern = new float[] { lineWidth, lineWidth * 2 };
        graphics.DrawLine(pen, new System.Drawing.PointF(textLocation.X + width, textLocation.Y), pageNumberLocation);
    }
    
    // draw page number
    graphics.DrawString(pageNumber.ToString(), font, fontSize, brush, pageNumberLocation);
    graphics.MeasureString(pageNumber.ToString(), font, fontSize, out width, out height);

    return new System.Drawing.RectangleF(textLocation.X, textLocation.Y, pageNumberLocation.X - textLocation.X + width, height);
}

/// <summary>
/// Adds the image to PDF document.
/// </summary>
/// <param name="document">The document.</param>
/// <param name="image">The image.</param>
private static void AddImageToPdfDocument(
    Vintasoft.Imaging.Pdf.PdfDocument document, 
    Vintasoft.Imaging.VintasoftImage image)
{
    // select compression
    Vintasoft.Imaging.Pdf.PdfCompression compression;
    Vintasoft.Imaging.Pdf.PdfCompressionSettings compressionSettings;
    if (image.BitsPerPixel == 1)
        compression = Vintasoft.Imaging.Pdf.PdfCompression.CcittFax;
    else if (image.PixelFormat == Vintasoft.Imaging.PixelFormat.Indexed8)
        compression = Vintasoft.Imaging.Pdf.PdfCompression.Zip;
    else
        compression = Vintasoft.Imaging.Pdf.PdfCompression.Jpeg;
    
    // create compression settings
    compressionSettings = new Vintasoft.Imaging.Pdf.PdfCompressionSettings();
    compressionSettings.JpegQuality = 75;
    
    // add page
    document.Pages.Add(image, compression, compressionSettings);
}