PDF Portfolio Creation with C# and .NET

PDF portfolios are an excellent way to organize and share multiple files within a single PDF container. They allow users to include various file types, such as PDFs, images, and documents, while maintaining their original structure and functionality. In this article, we will explore how to construct PDF portfolios from streams using C# and .NET, leveraging the powerful SautinSoft PDF.NET library.

A PDF portfolio is a collection of files embedded within a single PDF document. Unlike merging files into one continuous document, a portfolio keeps each file separate, allowing users to access and interact with them individually. This feature is particularly useful for creating professional presentations, organizing project files, or sharing multiple documents in a structured format.

Using streams to create PDF portfolios offers several advantages:

  • Efficiency. Streams allow you to work with files directly in memory, reducing the need for temporary storage.
  • Flexibility. You can handle files from various sources, such as databases, cloud storage, or user uploads.
  • Security. Streams enable you to process sensitive data without saving it to disk.

Step-by-step guide:

  1. Add SautinSoft.PDF from NuGet.
  2. Load the document and convert it to a PDF portfolio.
  3. Extract files from the ZIP archive to a directory.
  4. Add files and folders from the directory to the portfolio.
  5. Delete the extracted directory.
  6. Set the initial file for display and configure portfolio fields.
  7. Add a new portfolio field "Full Name".
  8. Save the document.

Input file:

Intermediate folder:

Output result:

Complete code

using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using SautinSoft.Pdf.Objects;
using SautinSoft.Pdf.Portfolios;
using SautinSoft.Pdf;

class Program
{
    /// <summary>
    /// Create PDF Portfolios.
    /// </summary>
    /// <remarks>
    /// Details: https://sautinsoft.com/products/pdf/help/net/developer-guide/create-pdf-portfolios-from-stream.php
    /// </remarks>
    static void Main()
    {
        // Before starting this example, please get a free 100-day trial key:
        // https://sautinsoft.com/start-for-free/

        // Apply the key here:
        // PdfDocument.SetLicense("...");

        using (var document = PdfDocument.Load(Path.GetFullPath(@"..\..\..\PortfolioTemplate.pdf")))
        {
            // Make the document a PDF portfolio (a collection of file attachments).
            var portfolio = document.SetPortfolio();

            // Add all files and folders from the zip archive to the portfolio.
            using (var archiveStream = File.OpenRead(@"..\..\..\Attachments.zip"))
            using (var archive = new ZipArchive(archiveStream, ZipArchiveMode.Read, leaveOpen: true))
                foreach (var entry in archive.Entries)
                {
                    // Get or create portfolio folder hierarchy from the zip entry full name.
                    var folder = GetOrAddFolder(portfolio, entry.FullName);

                    if (!string.IsNullOrEmpty(entry.Name))
                    {
                        // Zip archive entry is a file.
                        var files = folder == null ? portfolio.Files : folder.Files;

                        var embeddedFile = files.AddEmpty(entry.Name).EmbeddedFile;

                        // Set the portfolio file size and modification date.
                        if (entry.Length < int.MaxValue)
                            embeddedFile.Size = (int)entry.Length;
                        embeddedFile.ModificationDate = entry.LastWriteTime;

                        // Copy portfolio file contents from the zip archive entry.
                        // Portfolio file is compressed if its compressed size in the zip archive is less than its uncompressed size.
                        using (var entryStream = entry.Open())
                        using (var embeddedFileStream = embeddedFile.OpenWrite(compress: entry.CompressedLength < entry.Length))
                            entryStream.CopyTo(embeddedFileStream);
                    }
                    else
                        // Zip archive entry is a folder.
                        // Set the portfolio folder modification date.
                        folder.ModificationDate = entry.LastWriteTime;
                }

            // Set the first PDF file contained in the portfolio to be initially presented in the user interface.
            // Note that all files contained in the portfolio are also contained in the PdfDocument.EmbeddedFiles.
            portfolio.InitialFile = document.EmbeddedFiles.Select(entry => entry.Value).FirstOrDefault(fileSpec => fileSpec.Name.EndsWith(".pdf", StringComparison.Ordinal));

            // Hide all existing portfolio fields except 'Size'.
            foreach (var portfolioFieldEntry in portfolio.Fields)
                portfolioFieldEntry.Value.Hidden = portfolioFieldEntry.Value.Name != "Size";

            // Add a new portfolio field with display name 'Full Name' and it should be in the first column.
            var portfolioFieldKeyAndValue = portfolio.Fields.Add(PdfPortfolioFieldDataType.String, "FullName");
            var portfolioField = portfolioFieldKeyAndValue.Value;
            portfolioField.Name = "Full Name";
            portfolioField.Order = 0;

            // For each file and folder in the portfolio, set FullName field value to the relative path of the file/folder in the zip archive.
            SetFullNameFieldValue(portfolio.Files, portfolio.Folders, string.Empty, portfolioFieldKeyAndValue.Key);

            document.Save("Portfolio from Streams.pdf");
        }

        System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo("Portfolio from Streams.pdf") { UseShellExecute = true });
    }

    static PdfPortfolioFolder GetOrAddFolder(PdfPortfolio portfolio, string fullName)
    {
        var folderNames = fullName.Split('/');

        PdfPortfolioFolder folder = null;
        var folders = portfolio.Folders;

        // Last name is the name of the file, so it is skipped.
        for (int i = 0; i < folderNames.Length - 1; ++i)
        {
            // Get or add folder with the specific name.
            var folderName = folderNames[i];
            folder = folders.FirstOrDefault(f => f.Name == folderName);
            if (folder == null)
                folder = folders.AddEmpty(folderName);

            folders = folder.Folders;
        }

        return folder;
    }

    static void SetFullNameFieldValue(PdfPortfolioFileCollection files, PdfPortfolioFolderCollection folders, string parentFolderFullName, PdfName portfolioFieldKey)
    {
        // Set FullName field value for all the fields.
        foreach (var fileSpecification in files)
            fileSpecification.PortfolioFieldValues.Add(portfolioFieldKey, new PdfPortfolioFieldValue(parentFolderFullName + fileSpecification.Name));

        foreach (var folder in folders)
        {
            // Set FullName field value for the folder.
            var folderFullName = parentFolderFullName + folder.Name + '/';
            folder.PortfolioFieldValues.Add(portfolioFieldKey, new PdfPortfolioFieldValue(folderFullName));

            // Recursively set FullName field value for all files and folders underneath the current portfolio folder.
            SetFullNameFieldValue(folder.Files, folder.Folders, folderFullName, portfolioFieldKey);
        }
    }
}

Download

Option Infer On

Imports System
Imports System.IO
Imports System.IO.Compression
Imports System.Linq
Imports SautinSoft.Pdf.Objects
Imports SautinSoft.Pdf.Portfolios
Imports SautinSoft.Pdf

Friend Class Program
	''' <summary>
	''' Create PDF Portfolios.
	''' </summary>
	''' <remarks>
	''' Details: https://sautinsoft.com/products/pdf/help/net/developer-guide/create-pdf-portfolios-from-stream.php
	''' </remarks>
	Shared Sub Main()
		' Before starting this example, please get a free 100-day trial key:
		' https://sautinsoft.com/start-for-free/

		' Apply the key here:
		' PdfDocument.SetLicense("...");

		Using document = PdfDocument.Load(Path.GetFullPath("..\..\..\PortfolioTemplate.pdf"))
			' Make the document a PDF portfolio (a collection of file attachments).
			Dim portfolio = document.SetPortfolio()

			' Add all files and folders from the zip archive to the portfolio.
			Using archiveStream = File.OpenRead("..\..\..\Attachments.zip")
			Using archive = New ZipArchive(archiveStream, ZipArchiveMode.Read, leaveOpen:= True)
				For Each entry In archive.Entries
					' Get or create portfolio folder hierarchy from the zip entry full name.
					Dim folder = GetOrAddFolder(portfolio, entry.FullName)

					If Not String.IsNullOrEmpty(entry.Name) Then
						' Zip archive entry is a file.
						Dim files = If(folder Is Nothing, portfolio.Files, folder.Files)

						Dim embeddedFile = files.AddEmpty(entry.Name).EmbeddedFile

						' Set the portfolio file size and modification date.
						If entry.Length < Integer.MaxValue Then
							embeddedFile.Size = CInt(entry.Length)
						End If
						embeddedFile.ModificationDate = entry.LastWriteTime

						' Copy portfolio file contents from the zip archive entry.
						' Portfolio file is compressed if its compressed size in the zip archive is less than its uncompressed size.
						Using entryStream = entry.Open()
						Using embeddedFileStream = embeddedFile.OpenWrite(compress:= entry.CompressedLength < entry.Length)
							entryStream.CopyTo(embeddedFileStream)
						End Using
						End Using
					Else
						' Zip archive entry is a folder.
						' Set the portfolio folder modification date.
						folder.ModificationDate = entry.LastWriteTime
					End If
				Next entry
			End Using
			End Using

			' Set the first PDF file contained in the portfolio to be initially presented in the user interface.
			' Note that all files contained in the portfolio are also contained in the PdfDocument.EmbeddedFiles.
			portfolio.InitialFile = document.EmbeddedFiles.Select(Function(entry) entry.Value).FirstOrDefault(Function(fileSpec) fileSpec.Name.EndsWith(".pdf", StringComparison.Ordinal))

			' Hide all existing portfolio fields except 'Size'.
			For Each portfolioFieldEntry In portfolio.Fields
				portfolioFieldEntry.Value.Hidden = portfolioFieldEntry.Value.Name <> "Size"
			Next portfolioFieldEntry

			' Add a new portfolio field with display name 'Full Name' and it should be in the first column.
			Dim portfolioFieldKeyAndValue = portfolio.Fields.Add(PdfPortfolioFieldDataType.String, "FullName")
			Dim portfolioField = portfolioFieldKeyAndValue.Value
			portfolioField.Name = "Full Name"
			portfolioField.Order = 0

			' For each file and folder in the portfolio, set FullName field value to the relative path of the file/folder in the zip archive.
			SetFullNameFieldValue(portfolio.Files, portfolio.Folders, String.Empty, portfolioFieldKeyAndValue.Key)

			document.Save("Portfolio from Streams.pdf")
		End Using

		System.Diagnostics.Process.Start(New System.Diagnostics.ProcessStartInfo("Portfolio from Streams.pdf") With {.UseShellExecute = True})
	End Sub

	Private Shared Function GetOrAddFolder(ByVal portfolio As PdfPortfolio, ByVal fullName As String) As PdfPortfolioFolder
		Dim folderNames = fullName.Split("/"c)

		Dim folder As PdfPortfolioFolder = Nothing
		Dim folders = portfolio.Folders

		' Last name is the name of the file, so it is skipped.
		For i As Integer = 0 To folderNames.Length - 2
			' Get or add folder with the specific name.
			Dim folderName = folderNames(i)
			folder = folders.FirstOrDefault(Function(f) f.Name = folderName)
			If folder Is Nothing Then
				folder = folders.AddEmpty(folderName)
			End If

			folders = folder.Folders
		Next i

		Return folder
	End Function

	Private Shared Sub SetFullNameFieldValue(ByVal files As PdfPortfolioFileCollection, ByVal folders As PdfPortfolioFolderCollection, ByVal parentFolderFullName As String, ByVal portfolioFieldKey As PdfName)
		' Set FullName field value for all the fields.
		For Each fileSpecification In files
			fileSpecification.PortfolioFieldValues.Add(portfolioFieldKey, New PdfPortfolioFieldValue(parentFolderFullName & fileSpecification.Name))
		Next fileSpecification

		For Each folder In folders
			' Set FullName field value for the folder.
			Dim folderFullName = parentFolderFullName & folder.Name & "/"c
			folder.PortfolioFieldValues.Add(portfolioFieldKey, New PdfPortfolioFieldValue(folderFullName))

			' Recursively set FullName field value for all files and folders underneath the current portfolio folder.
			SetFullNameFieldValue(folder.Files, folder.Folders, folderFullName, portfolioFieldKey)
		Next folder
	End Sub
End Class

Download


If you need a new code example or have a question: email us at support@sautinsoft.com or ask at Online Chat (right-bottom corner of this page) or use the Form below:



Questions and suggestions from you are always welcome!

We are developing .Net components since 2002. We know PDF, DOCX, RTF, HTML, XLSX and Images formats. If you need any assistance with creating, modifying or converting documents in various formats, we can help you. We will write any code example for you absolutely free.