using System.IO; using TSpdf.Kernel.Pdf; using TSpdf.Kernel.Utils; using TSpdf.Pdfa; namespace tsPDFUtilsCore { public class Utilidades { public static MemoryStream crearPDFA(Stream pdfOrigen) { var reader = new PdfReader(pdfOrigen); MemoryStream ms = new MemoryStream(); var writerProps = new WriterProperties(); writerProps.SetPdfVersion(PdfVersion.PDF_2_0); writerProps.UseSmartMode(); var writer = new PdfWriter(ms, writerProps); writer.SetSmartMode(true); // Necesario para que el memoryStream no se cierre, y no de una execpción donde se llame a la función writer.SetCloseStream(false); var sourcePDF = new PdfDocument(reader); bool esRGB = recorrerPaginas(sourcePDF); // Se recorre las páginas para comprobar que no falte la anotación F, en tal caso la mete for (int page = 1; page <= sourcePDF.GetNumberOfPages(); page++) { var paginaActual = sourcePDF.GetPage(page); var anotaciones = paginaActual.GetAnnotations(); if (anotaciones != null) { foreach (var anotacionActual in anotaciones) { var diccionario = anotacionActual.GetPdfObject(); if (!diccionario.ContainsKey(PdfName.F)) { diccionario.Put(PdfName.F, new PdfNumber(4)); } } } } PdfADocument disPDF; if (esRGB) { disPDF = new PdfADocument(writer, PdfAConformanceLevel.PDF_A_3B, new PdfOutputIntent("Custom" , "", "https://www.color.org", "sRGB", new MemoryStream(Properties.Resources.sRGB2014))); } else { disPDF = new PdfADocument(writer, PdfAConformanceLevel.PDF_A_3B, new PdfOutputIntent("Custom" , "", "https://www.color.org", "FOGRA39", new MemoryStream(Properties.Resources.fogra39L))); } disPDF.InitializeOutlines(); // Configurar parámetros requeridos disPDF.SetTagged(); disPDF.GetCatalog().SetLang(new PdfString("es-ES")); disPDF.GetCatalog().SetViewerPreferences(new PdfViewerPreferences().SetDisplayDocTitle(true)); var merger = new PdfMerger(disPDF, true,true); merger.Merge(sourcePDF, 1, sourcePDF.GetNumberOfPages()); sourcePDF.Close(); disPDF.Close(); reader.Close(); writer.Close(); // Esto hace falta para volver al inicio del memorystream ms.Position = 0; return ms; } static bool controlarInterpolate(PdfDictionary resources, bool tieneRGB) { var xObjects = resources?.GetAsDictionary(PdfName.XObject); bool tieneRGBActual = tieneRGB; foreach (var key in xObjects.KeySet()) { var stream = xObjects.GetAsStream(key); // se mira que tenga el prefijo subtype qye es la forma como el pdf pone las imagenes y los formularios -> /Subtype /Image ó /Subtype /Form var subtype = stream.GetAsName(PdfName.Subtype); // En caso de que sea una imagen, pone el interpolate a false, que es lo que causaba el fallo en alguno de los pdfs if (PdfName.Image.Equals(subtype)) { if (stream.GetAsBoolean(PdfName.Interpolate)?.GetValue() == true) { stream.Put(PdfName.Interpolate, PdfBoolean.FALSE); } // Detectar RGB var colorSpace = stream.Get(PdfName.ColorSpace); if (colorSpace != null && colorSpace.Equals(PdfName.DeviceRGB)) { tieneRGBActual = true; tieneRGB = true; } } // En caso de que sea un formulario, vuelve a llamar al metodo para buscar imagenes dentro else if (PdfName.Form.Equals(subtype)) { var formRes = stream.GetAsDictionary(PdfName.Resources); if (formRes != null) { bool hijoTieneRGB = controlarInterpolate(formRes, tieneRGBActual); if (hijoTieneRGB) { tieneRGBActual = true; } } } } return tieneRGBActual; } static bool recorrerPaginas(PdfDocument pdf) { bool tieneRGB = false; for (int i = 1; i <= pdf.GetNumberOfPages(); i++) { bool rgbMinimo = false; var diccPaginaActual = pdf.GetPage(i).GetResources().GetPdfObject(); rgbMinimo = controlarInterpolate(diccPaginaActual, tieneRGB); if (rgbMinimo) { tieneRGB = true; } } return tieneRGB; } } }