Skip to content

Instantly share code, notes, and snippets.

@swalex
Last active June 4, 2025 22:32
Show Gist options
  • Save swalex/34a6213897958334869ca5d37d7e0177 to your computer and use it in GitHub Desktop.
Save swalex/34a6213897958334869ca5d37d7e0177 to your computer and use it in GitHub Desktop.
Encapsulate (save) PDF with FO-DICOM (into a DCM file)
public static void Encapsulate( Patient patient, Study study, Institution institution, string directory, string filename, byte[] pdf )
{
var name = new DicomPersonName( DicomTag.PatientName, patient.LastName, patient.FirstName,
patient.MiddleName, patient.NamePrefix, patient.NameSuffix );
var studyUID = new DicomUID( study.InstanceUID, "Study Instance UID", DicomUidType.SOPInstance );
var company = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyCompanyAttribute>().Company;
var product = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyProductAttribute>().Product;
DicomDataset[] refItems = study.Series
.SelectMany( s => s.Datasets )
.Select( d => new DicomDataset
{
{ DicomTag.ReferencedSOPClassUID, d.ClassUID },
{ DicomTag.ReferencedSOPInstanceUID, d.InstanceUID }
} ).ToArray();
var sourceInstances = new DicomSequence( DicomTag.SourceInstanceSequence, refItems );
var conceptName = new DicomSequence( DicomTag.ConceptNameCodeSequence );
var generator = new DicomUIDGenerator();
var dataset = new DicomDataset
{
{ DicomTag.SpecificCharacterSet, series.SpecificCharacterSet },
{ DicomTag.InstanceCreationDate, DateTime.Now },
{ DicomTag.InstanceCreationTime, DateTime.Now },
{ DicomTag.SOPClassUID, DicomUID.EncapsulatedPDFStorage },
{ DicomTag.SOPInstanceUID, generator.Generate() },
{ DicomTag.StudyDate, study.DateTime.Date },
{ DicomTag.ContentDate, DateTime.Now },
{ DicomTag.AcquisitionDateTime, DateTime.Now },
{ DicomTag.StudyTime, study.DateTime },
{ DicomTag.ContentTime, DateTime.Now },
{ DicomTag.AccessionNumber, study.AccessionNumber.ToString( CultureInfo.InvariantCulture ) },
{ DicomTag.Modality, "DOC" },
{ DicomTag.ConversionType, "WSD" },
{ DicomTag.ImageLaterality, planning.PatientSide },
{ DicomTag.Manufacturer, company },
{ DicomTag.ManufacturerModelName, product },
{ DicomTag.ReferringPhysicianName, (string)null },
{ DicomTag.PatientID, patient.ID },
{ DicomTag.PatientBirthDate, patient.BirthDate },
{ DicomTag.PatientSex, patient.Sex },
{ DicomTag.StudyInstanceUID, study.InstanceUID },
{ DicomTag.StudyDescription, study.Description },
{ DicomTag.SeriesInstanceUID, generator.Generate( studyUID ) },
{ DicomTag.SeriesDescription, $"{product} {planning.Description}" },
{ DicomTag.PerformingPhysicianName, Environment.UserName },
{ DicomTag.SeriesDate, DateTime.Now },
{ DicomTag.SeriesTime, DateTime.Now },
{ DicomTag.StudyID, study.ID },
{ DicomTag.SeriesNumber, planning.Series.OrderBy( s => s.Number ).Last().Number + 1 },
{ DicomTag.InstanceNumber, 1 },
{ DicomTag.BurnedInAnnotation, "YES" },
{ DicomTag.DocumentTitle, $"{product} Generated Document" },
{ DicomTag.VerificationFlag, "UNVERIFIED" },
{ DicomTag.MIMETypeOfEncapsulatedDocument, "application/pdf" },
name,
sourceInstances,
conceptName,
{ DicomTag.EncapsulatedDocument, pdf }
};
dataset.Add( DicomTag.InstitutionName, institution.Name );
dataset.Add( DicomTag.InstitutionAddress, institution.Address );
dataset.Add( DicomTag.InstitutionalDepartmentName, institution.Department );
var dicomFile = new DicomFile( dataset );
using ( var file = new FileStream( Path.Combine( directory, filename ), FileMode.Create ) )
{
dicomFile.Save( file );
}
}
@Elior-Notal
Copy link

Hi Alex,
thank you for that! Can you please share the complete code (with the implementations of Patient, Study and Institution classes)?
thanks

@mbahrami
Copy link

For those interested in a self-contained function creating an PDF Encapsulated Dicom file based on above you can use the following:

public void Encapsulate()
{
string dicom_file = @"C:\DicomFiles\Encapsulated.dcm";
string pdf_file = @"C:\DicomFiles\example.pdf";
byte[] pdf = System.IO.File.ReadAllBytes(pdf_file);

var studyUID = new DicomUID("1234", "Study Instance UID", DicomUidType.SOPInstance);                   
var generator = new DicomUIDGenerator();
var dataset = new DicomDataset
{
      { DicomTag.SpecificCharacterSet,  "ISO_IR 6" },
      { DicomTag.InstanceNumber, 1 },
      { DicomTag.InstanceCreationDate, DateTime.Now },
      { DicomTag.InstanceCreationTime, DateTime.Now },
      { DicomTag.SOPClassUID, DicomUID.EncapsulatedPDFStorage },
      { DicomTag.SOPInstanceUID, "" },
      { DicomTag.ContentDate, DateTime.Now },
      { DicomTag.ContentTime, DateTime.Now },
      { DicomTag.AcquisitionDateTime, DateTime.Now },                           
      { DicomTag.AccessionNumber, "1234"},
      { DicomTag.Modality, "DOC" },
      { DicomTag.ConversionType, "WSD" },
      { DicomTag.ImageLaterality, "" },
      { DicomTag.Manufacturer, "Manufacturer" },
      { DicomTag.ManufacturerModelName, "Product" },

      { DicomTag.ReferringPhysicianName, (string)null },
      { DicomTag.PerformingPhysicianName, "physician" }, 

      { DicomTag.PatientName, "Patient Name"},
      { DicomTag.PatientID, "" },
      { DicomTag.PatientBirthDate, "" },
      { DicomTag.PatientSex, "M" },
      { DicomTag.PatientAge, "" },
      { DicomTag.PatientWeight, "" },
      { DicomTag.PatientAddress, "" },

      { DicomTag.StudyID, "2345" },
      { DicomTag.StudyDate, "0095.07.24" },
      { DicomTag.StudyTime, "093900" },
      { DicomTag.StudyDescription, "" },

      { DicomTag.SeriesNumber, "1234" },
      { DicomTag.SeriesDate, DateTime.Now },
      { DicomTag.SeriesTime, DateTime.Now },
      { DicomTag.SeriesDescription, ""},
      { DicomTag.SeriesInstanceUID, generator.Generate( studyUID ) },    
   
      { DicomTag.BurnedInAnnotation, "YES" },
      { DicomTag.VerificationFlag, "VERIFIED" },
      { DicomTag.MIMETypeOfEncapsulatedDocument, "application/pdf" },         
      { DicomTag.EncapsulatedDocument, pdf }
};

DicomFile file = new DicomFile();           
file.Dataset.Add(dataset);
file.FileMetaInfo.TransferSyntax = DicomTransferSyntax.ImplicitVRLittleEndian; //Specify transfer syntax
file.Save(dicom_file);

}

@githubsamurai
Copy link

this is SO awesome! Thanks all!

@githubsamurai
Copy link

i notice that when i view the resulting DICOM file/image? with the PDF embedded (using MicroDICOM) i have to click on the PDF to open it in an external reader... will it also work like this if imported into Fuji or if viewed in something like Clear Canvas?

@Nikhilthambi
Copy link

Nikhilthambi commented Sep 26, 2023

Hi Alex, Thanks for your reference I did pdf upload using this code its great, I have one another question, do you know how to create the KeyImages in dicom in the same way. it would be great if you can provide a sample like pdf upload

@AleksandrKuznietsov
Copy link

AleksandrKuznietsov commented Jan 14, 2025

Thanks for the code.
I used it in my application with some modifications.
But when sending files to the DCM4CHEE-ARC server, I often received an error like Failure [0110: Processing failure] -> java.io.EOFException.
For a long time I could not understand why some files are sent successfully, while others return an error.
It turned out that the reason is in the length of the PDF file. If it is odd, the server does not accept it.
I solved it this way:

byte[] pdf = await File.ReadAllBytesAsync(pdfFilePath);
if (pdf.Length % 2 == 0)
{
    pdfDataset.Add(DicomTag.EncapsulatedDocument, pdf);
}
else
{
    byte[] pdfEven = new byte[pdf.Length + 1];
    for (int i = 0; i < pdf.Length; i++)
    {
        pdfEven[i] = pdf[i];
    }
    pdfEven[pdf.Length] = 0;

    pdfDataset.Add(DicomTag.EncapsulatedDocument, pdfEven);
}              

After this, all encapsulated PDFs are accepted without errors.

Best regards,
Alieksandr Kuznietsov

@yebYazilim
Copy link

yebYazilim commented Jun 4, 2025

Association Abort [source: ServiceProvider; reason: InvalidPDUParameter] I get an error, what could be the cause? I get this error when sending to pacs server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment