HiQPdf HTML to PDF Converter can embed arbitrary files directly into the generated PDF as document-level attachments. The attached files appear in the Attachments panel of the viewer and travel with the document. The receiver does not need access to the original source files.
Attachments are configured through the PdfDocumentControl object exposed by the HtmlToPdfDocument property. Each attachment is created as a PdfFileAttachment instance and then registered with PdfDocumentControlAddFileAttachment(PdfFileAttachment). You can call AddFileAttachment multiple times to embed several files in a single conversion.
PdfFileAttachment instances are obtained through static factory methods. Use PdfFileAttachmentFromBytes(Byte, String) when the payload is built in memory. The first argument is the byte buffer. The second argument is the file name shown in the Attachments panel. Use PdfFileAttachmentFromFile(String) when the payload is already on disk. The file is read at registration time and embedded in the document. The file name in the Attachments panel is taken from the path.
Two additional factory methods exist for non-embedded references. PdfFileAttachmentFromExternalPath(String) stores a reference to an external file path. PdfFileAttachmentFromUrl(String) stores a reference to a URL. Neither variant embeds the bytes. The resulting PDF is not portable and is not allowed under any PDF/A standard. Prefer the embedding variants for most workflows.
After creation each attachment can be tagged with metadata. The MimeType property helps viewers select a helper application when the attachment is opened. The Description property is shown as the tooltip in the Attachments panel. The Relationship property describes how the attached file relates to the PDF content. It is relevant when the target standard requires it.
Attachment support depends on the target PDF standard configured through PdfDocumentControlPdfStandard. PDF/A-2 (PdfA2b, PdfA2a) and PDF/A-4 base conformance (PdfA4, with their PDF/UA combinations) restrict embedded files to PDF/A-conformant content only. Calling AddFileAttachment with an arbitrary file type under one of those standards throws an InvalidOperationException at save time. To attach XML, CSV, XLSX or similar arbitrary file types use the PDF/A-3 family (PdfA3b, PdfA3u, PdfA3a, PdfUa1PdfA3a) or PDF/A-4f (PdfA4f, PdfUa2PdfA4f). PDF/UA-1 and PDF/UA-2 by themselves do not restrict attachments.
Under the standards that require it, the library writes the relationship metadata automatically. When Relationship is left at its default PdfAttachmentRelationshipUnspecified value, the engine defaults it to PdfAttachmentRelationshipSource. Setting Relationship explicitly is recommended for clarity but is not strictly required. The PdfAttachmentRelationshipEncryptedPayload value is only valid under PdfA4f and PdfUa2PdfA4f. Using it under any other standard throws an InvalidOperationException.
Setting PdfDocumentControlViewer.PageMode to PdfPageModeAttachments instructs the viewer to open the Attachments panel automatically when the document is loaded. This is convenient when the attachments are the primary payload of the document.
Use PdfFileAttachmentFromBytes(Byte, String) to embed a byte buffer that was assembled in memory. Typical sources include the output of a serializer, a generated CSV string or the output of another converter.
MimeType and Description are optional but recommended. Viewers use the MimeType to pick the right helper application when the attachment is opened. The Description appears as a tooltip in the Attachments panel.
byte[] xmlBytes = Encoding.UTF8.GetBytes(BuildSampleXml());
var xmlAttachment = PdfFileAttachment.FromBytes(xmlBytes, "data.xml");
xmlAttachment.MimeType = "application/xml";
xmlAttachment.Description = "Source XML data";
xmlAttachment.Relationship = PdfAttachmentRelationship.Source;
htmlToPdfConverter.Document.AddFileAttachment(xmlAttachment);Use PdfFileAttachmentFromFile(String) when the payload is already on disk. The factory reads the file at registration time and embeds its bytes. The file name shown in the Attachments panel is taken from the path.
string alphabetFilePath = Path.Combine(GetDemoTextsPath(), "Alphabet.txt");
var textAttachment = PdfFileAttachment.FromFile(alphabetFilePath);
textAttachment.MimeType = "text/plain";
textAttachment.Description = "Sample alphabet text";
htmlToPdfConverter.Document.AddFileAttachment(textAttachment);Whether and how an attachment is preserved depends on the standard configured through PdfDocumentControlPdfStandard:
None, PdfUa1, PdfUa2. Any attachment is accepted with no relationship requirement.
PdfA2b, PdfA2a, PdfUa1PdfA2b, PdfUa1PdfA2a, PdfUa2PdfA2b, PdfUa2PdfA2a. Embedded files are restricted to PDF/A-conformant content. Attaching arbitrary file types throws InvalidOperationException at save time.
PdfA4, PdfUa2PdfA4. Same restriction as PDF/A-2. Use PdfA4f or PdfUa2PdfA4f instead to attach arbitrary files under the PDF 2.0 archival rules.
PdfA3b, PdfA3u, PdfA3a, PdfUa1PdfA3a, PdfA4f, PdfUa2PdfA4f. Attachments of arbitrary type are accepted. The relationship metadata is required and the engine defaults Relationship to PdfAttachmentRelationshipSource when it is left as Unspecified. Set Relationship explicitly to one of the HiQPdf.NextPdfAttachmentRelationship values to override the default.
using System.IO;
using System.Text;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using HiQPdf_Next_AspNetDemo.Models;
using HiQPdf.Next;
namespace HiQPdf_Next_AspNetDemo.Controllers
{
public class AddAttachmentsToGeneratedPdfController : Controller
{
IWebHostEnvironment m_hostingEnvironment;
public AddAttachmentsToGeneratedPdfController (IWebHostEnvironment hostingEnvironment)
{
m_hostingEnvironment = hostingEnvironment;
}
public IActionResult Index()
{
var model = SetViewModel();
return View(model);
}
[HttpPost]
public ActionResult ConvertToPdf(AddAttachmentsToGeneratedPdfViewModel model)
{
if (!ModelState.IsValid)
{
var errorMessage = ModelStateHelper.GetModelErrors(ModelState);
throw new ValidationException(errorMessage);
}
// Set the serial number received after purchase to use the library in licensed mode; leave it commented for demo mode
// Licensing.SerialNumber = "your-serial-number";
// Create an HTML to PDF converter object with default settings
HtmlToPdf htmlToPdfConverter = new HtmlToPdf();
// Open the Attachments panel on document load
htmlToPdfConverter.Document.Viewer.PageMode = PdfPageMode.Attachments;
// Sets the PDF standard for the generated document
// Leave as None to generate a plain PDF without an accessibility structure tree or archival metadata
htmlToPdfConverter.Document.PdfStandard = model.PdfStandard;
// ===== Document-level attachments =====
// Attachments added via PdfDocumentOptions.AddFileAttachment appear
// in the viewer's Attachments panel (opened with the paperclip icon
// in Acrobat's left sidebar). They have no visible marker on any
// page. Two factory methods cover the common cases: FromBytes for
// in-memory data and FromFile for files on disk
// FromBytes -- embed an in-memory XML invoice
byte[] invoiceXmlBytes = Encoding.UTF8.GetBytes(BuildSampleInvoiceXml());
var xmlAttachment = PdfFileAttachment.FromBytes(invoiceXmlBytes, "invoice.xml");
xmlAttachment.MimeType = "application/xml";
xmlAttachment.Description = "Source XML invoice data";
xmlAttachment.Relationship = PdfAttachmentRelationship.Source;
htmlToPdfConverter.Document.AddFileAttachment(xmlAttachment);
// FromFile -- embed a file from disk
string alphabetFilePath = Path.Combine(GetDemoTextsPath(), "Alphabet.txt");
var textAttachment = PdfFileAttachment.FromFile(alphabetFilePath);
textAttachment.MimeType = "text/plain";
textAttachment.Description = "Sample alphabet text";
htmlToPdfConverter.Document.AddFileAttachment(textAttachment);
// convert HTML to PDF
byte[] pdfBuffer = null;
if (model.UrlOrHtmlCode == "ConvertUrl")
{
// convert URL to a PDF memory buffer
string url = model.Url;
pdfBuffer = htmlToPdfConverter.ConvertUrlToMemory(url);
}
else
{
// convert HTML code
string htmlCode = model.HtmlCode;
string baseUrl = model.BaseUrl;
// convert HTML code to a PDF memory buffer
pdfBuffer = htmlToPdfConverter.ConvertHtmlToMemory(htmlCode, baseUrl);
}
FileResult fileResult = new FileContentResult(pdfBuffer, "application/pdf");
fileResult.FileDownloadName = "PdfAttachmentsDemo.pdf";
return fileResult;
}
private AddAttachmentsToGeneratedPdfViewModel SetViewModel()
{
var model = new AddAttachmentsToGeneratedPdfViewModel();
var contentRootPath = Path.Combine(m_hostingEnvironment.ContentRootPath, "wwwroot");
model.HtmlCode = System.IO.File.ReadAllText(Path.Combine(contentRootPath, "DemoFiles/Html/PDF_Standards.html"));
return model;
}
// ===== Sample data builders =====
private static string BuildSampleInvoiceXml()
{
return
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<Invoice>\n" +
" <InvoiceNumber>2026-0042</InvoiceNumber>\n" +
" <IssueDate>2026-05-18</IssueDate>\n" +
" <Customer>Acme Corporation</Customer>\n" +
" <Items>\n" +
" <Item><Name>Widget</Name><Quantity>10</Quantity><UnitPrice>1.50</UnitPrice></Item>\n" +
" <Item><Name>Gadget</Name><Quantity>5</Quantity><UnitPrice>3.75</UnitPrice></Item>\n" +
" <Item><Name>Sprocket</Name><Quantity>2</Quantity><UnitPrice>12.00</UnitPrice></Item>\n" +
" </Items>\n" +
" <Total>57.75</Total>\n" +
"</Invoice>\n";
}
private string GetDemoFilesPath() => m_hostingEnvironment.ContentRootPath + "/wwwroot" + "/DemoFiles/";
private string GetDemoTextsPath() => Path.Combine(GetDemoFilesPath(), "Text");
}
}