documentImages;
float[] STYLE_SOLID = new float[] { 1, 0 };//0
float[] STYLE_NONE = null;//1
@@ -312,7 +315,11 @@ private void loadPrinterSettingsProps(String iniFile, String form, String printe
printerSettings.setupProperty(form, Const.COLOR, color + "");
printerSettings.setupProperty(form, Const.DUPLEX, duplex + "");
}
-
+ internal static void SetDefaultComplianceLevel(PdfConformanceLevel level)
+ {
+ if (props!=null)
+ props.setGeneralProperty(Const.COMPLIANCE_LEVEL, level.ToString());
+ }
private void loadProps()
{
if (props == null)
@@ -350,6 +357,7 @@ private void loadProps()
props.setupGeneralProperty(Const.MARGINS_INSIDE_BORDER, Const.DEFAULT_MARGINS_INSIDE_BORDER.ToString().ToLower());
props.setupGeneralProperty(Const.OUTPUT_FILE_DIRECTORY, ".");
props.setupGeneralProperty(Const.LEADING, "2");
+ props.setupGeneralProperty(Const.COMPLIANCE_LEVEL, PdfConformanceLevel.None.ToString());
props.setupGeneralProperty(Const.RUN_DIRECTION, Const.RUN_DIRECTION_LTR);
props.setupGeneralProperty(Const.JUSTIFIED_TYPE_ALL, "false");
@@ -415,16 +423,24 @@ private void init()
try
{
writer = PdfWriter.GetInstance(document, outputStream);
+ string level = props.getGeneralProperty(Const.COMPLIANCE_LEVEL);
+ if (Enum.TryParse(level, true, out complianceLevel))
+ {
+ if (SetComplainceLevel(complianceLevel))
+ writer.SetTagged();
+ }
+ document.Open();
+
}
catch (DocumentException de)
{
- GXLogging.Debug(log,"GxDrawRect error", de);
+ GXLogging.Debug(log, "init error", de);
}
- document.Open();
}
public void GxRVSetLanguage(String lang)
{
+ language = lang;
}
public void GxSetTextMode(int nHandle, int nGridX, int nGridY, int nPageLength)
@@ -795,6 +811,7 @@ public void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom
{
iTextSharp.text.Image image;
iTextSharp.text.Image imageRef;
+
if (documentImages != null && documentImages.TryGetValue(bitmap, out imageRef))
{
image = imageRef;
@@ -848,7 +865,9 @@ public void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom
image.ScaleToFit(rightAux - leftAux, bottomAux - topAux);
PdfContentByte cb = writer.DirectContent;
+ image.Alt = Path.GetFileName(bitmap);
cb.AddImage(image);
+
}
}
catch (DocumentException de)
@@ -1046,8 +1065,16 @@ public void GxAttris(String fontName, int fontSize, bool fontBold, bool fontItal
baseFont = CreateDefaultFont();
}
}
+ BaseFont defaultFont;
private BaseFont CreateDefaultFont() {
- return BaseFont.CreateFont("Helvetica", BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
+ if (defaultFont == null)
+ {
+ if (IsPdfA())
+ defaultFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.EMBEDDED);
+ else
+ defaultFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
+ }
+ return defaultFont;
}
private string GetFontLocation(string fontName)
{
@@ -1082,9 +1109,14 @@ private Hashtable GetFontLocations()
private bool IsEmbeddedFont(string fontName)
{
- bool generalEmbeedFont = props.getBooleanGeneralProperty(Const.EMBEED_SECTION, false);
- bool generalEmbeedNotSpecified = props.getBooleanGeneralProperty(Const.EMBEED_NOT_SPECIFIED_SECTION, false);
- return generalEmbeedFont && props.getBooleanProperty(Const.EMBEED_SECTION, fontName, generalEmbeedNotSpecified);
+ if (IsPdfA())
+ return true;
+ else
+ {
+ bool generalEmbeedFont = props.getBooleanGeneralProperty(Const.EMBEED_SECTION, false);
+ bool generalEmbeedNotSpecified = props.getBooleanGeneralProperty(Const.EMBEED_NOT_SPECIFIED_SECTION, false);
+ return generalEmbeedFont && props.getBooleanProperty(Const.EMBEED_SECTION, fontName, generalEmbeedNotSpecified);
+ }
}
private void LoadAsianFontsDll()
{
@@ -1970,7 +2002,22 @@ public void GxEndDocument()
}
}
+ if (IsPdfA())
+ {
+ using (Stream iccProfile = ReadResource("sRGB Color Space Profile.icm"))
+ {
+ ICC_Profile icc = ICC_Profile.GetInstance(iccProfile);
+ writer.SetOutputIntents("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", icc);
+ }
+ writer.ExtraCatalog.Put(PdfName.LANG, new PdfString(Config.GetCultureForLang(language).Name));
+ PdfDictionary markInfo = new PdfDictionary(PdfName.MARKINFO);
+ markInfo.Put(PdfName.MARKED, new PdfBoolean(PdfBoolean.TRUE));
+ writer.ExtraCatalog.Put(PdfName.MARKINFO, markInfo);
+
+ writer.CreateXmpMetadata();
+
+ }
document.Close();
@@ -2047,6 +2094,17 @@ public void GxEndDocument()
GXLogging.Debug(log,"GxEndDocument End");
}
+ Stream ReadResource(string fileName)
+ {
+ Assembly assembly = GetType().Assembly;
+ string resourcePath = $"{assembly.GetName().Name}.{fileName}";
+ return assembly.GetManifestResourceStream(resourcePath);
+ }
+
+ private bool IsPdfA()
+ {
+ return complianceLevel != 0;
+ }
public void GxEndPrinter()
{
@@ -2286,6 +2344,20 @@ private void SetSimpleColumn(ColumnText col, Rectangle rect)
{
col.SetSimpleColumn(rect.Left, rect.Bottom, rect.Right, rect.Top);
}
+ internal bool SetComplainceLevel(PdfConformanceLevel level)
+ {
+ switch (level)
+ {
+ case PdfConformanceLevel.Pdf_A1A:
+ writer.PDFXConformance = PdfWriter.PDFA1A;
+ return true;
+ case PdfConformanceLevel.Pdf_A1B:
+ writer.PDFXConformance = PdfWriter.PDFA1B;
+ return true;
+ default:
+ return false;
+ }
+ }
}
public class ParseINI
@@ -2737,7 +2809,8 @@ public class Const
public static String ADJUST_TO_PAPER = "AdjustToPaper"; //fit to page
public static String LINE_CAP_PROJECTING_SQUARE = "LineCapProjectingSquare";
public static String BARCODE128_AS_IMAGE = "Barcode128AsImage";
- public static String LEADING = "Leading";
+ public static String LEADING = "Leading";
+ internal static String COMPLIANCE_LEVEL = "ComplianceLevel";
//Printer settings
public static String PRINTER = "Printer";
@@ -3227,6 +3300,16 @@ public static ArrayList parseLine(String line, String separator)
}
}
-
+ internal enum PdfConformanceLevel
+ {
+ None,
+ Pdf_A1B,
+ Pdf_X1A2001,
+ Pdf_A1A,
+ Pdf_A2A,
+ Pdf_A2B,
+ Pdf_A3A,
+ Pdf_A3B
+ }
}
diff --git a/dotnet/src/dotnetframework/GxPdfReportsCS/sRGB Color Space Profile.icm b/dotnet/src/dotnetframework/GxPdfReportsCS/sRGB Color Space Profile.icm
new file mode 100644
index 000000000..7f9d18d09
Binary files /dev/null and b/dotnet/src/dotnetframework/GxPdfReportsCS/sRGB Color Space Profile.icm differ
diff --git a/dotnet/test/DotNetCoreWebUnitTest/DotNetCoreWebUnitTest.csproj b/dotnet/test/DotNetCoreWebUnitTest/DotNetCoreWebUnitTest.csproj
index 75b8a1aaa..bac7e207c 100644
--- a/dotnet/test/DotNetCoreWebUnitTest/DotNetCoreWebUnitTest.csproj
+++ b/dotnet/test/DotNetCoreWebUnitTest/DotNetCoreWebUnitTest.csproj
@@ -14,6 +14,8 @@
+
+
@@ -34,6 +36,7 @@
+
@@ -67,6 +70,12 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ Always
+
PreserveNewest
diff --git a/dotnet/test/DotNetCoreWebUnitTest/Middleware/WebReportTest.cs b/dotnet/test/DotNetCoreWebUnitTest/Middleware/WebReportTest.cs
new file mode 100644
index 000000000..c03d7d4ae
--- /dev/null
+++ b/dotnet/test/DotNetCoreWebUnitTest/Middleware/WebReportTest.cs
@@ -0,0 +1,61 @@
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Reflection;
+using System.Threading.Tasks;
+using Codeuctivity;
+using com.genexus.reports;
+using GeneXus.Metadata;
+using GeneXus.Programs;
+using Spire.Pdf;
+using Xunit;
+namespace xUnitTesting
+{
+ public class WebReportTest : MiddlewareTest
+ {
+ public WebReportTest() : base()
+ {
+ ClassLoader.FindType("apdfwebbasictest", "GeneXus.Programs", "apdfwebbasictest", Assembly.GetExecutingAssembly(), true);//Force loading assembly for webhook procedure
+ ClassLoader.FindType("apdfwebwoutimage", "GeneXus.Programs", "apdfwebwoutimage", Assembly.GetExecutingAssembly(), true);
+ server.AllowSynchronousIO = true;
+ }
+ [Fact(Skip = "temporary turned off due to timeout error")]
+ public void TestPDFA()
+ {
+ HttpClient client = server.CreateClient();
+ TestPDFA_1AB(client, "apdfwebbasictest.aspx", Spire.Pdf.PdfConformanceLevel.Pdf_A1A).GetAwaiter().GetResult();
+ TestPDFA_1AB(client, "apdfwebwoutimage.aspx", Spire.Pdf.PdfConformanceLevel.Pdf_A1A).GetAwaiter().GetResult();
+ PDFReportItextSharp.SetDefaultComplianceLevel(com.genexus.reports.PdfConformanceLevel.Pdf_A1B);
+ TestPDFA_1AB(client, "apdfwebbasictest.aspx", Spire.Pdf.PdfConformanceLevel.Pdf_A1B).GetAwaiter().GetResult();
+ TestPDFA_1AB(client, "apdfwebwoutimage.aspx", Spire.Pdf.PdfConformanceLevel.Pdf_A1B).GetAwaiter().GetResult();
+
+ }
+ async Task TestPDFA_1AB(HttpClient client, string serviceName, Spire.Pdf.PdfConformanceLevel expectedLevel)
+ {
+ HttpResponseMessage response = await client.GetAsync(serviceName);
+ response.EnsureSuccessStatusCode();
+ Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
+ String fileName = response.Content.Headers.ContentDisposition.FileName;
+ //Assert.Equal("Report.pdf", fileName);
+ using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
+ {
+ await response.Content.CopyToAsync(fs);
+ }
+ PdfDocument pdf = new PdfDocument();
+ pdf.LoadFromFile(fileName);
+ Spire.Pdf.PdfConformanceLevel conformance = pdf.Conformance;
+
+ Assert.True(expectedLevel == conformance, $"Conformance level is {conformance} but {expectedLevel} was expected");
+
+ PdfAValidator pdfAValidator = new PdfAValidator();
+ Report result = await pdfAValidator.ValidateWithDetailedReportAsync(fileName);
+ bool isValid = await pdfAValidator.ValidateAsync(fileName);
+ Assert.True(isValid, result.RawOutput);
+
+
+
+ }
+
+ }
+
+}
diff --git a/dotnet/test/DotNetCoreWebUnitTest/PDFReport.ini b/dotnet/test/DotNetCoreWebUnitTest/PDFReport.ini
new file mode 100644
index 000000000..10e61db72
--- /dev/null
+++ b/dotnet/test/DotNetCoreWebUnitTest/PDFReport.ini
@@ -0,0 +1,27 @@
+ComplianceLevel= pdf_a1a
+JustifiedTypeAll= false
+OutputFileDirectory= .
+ServerPrinting= false
+EmbeedNotSpecifiedFonts= false
+Leading= 2
+Embeed Fonts= false
+DottedStyle= 1;2
+LeftMargin= 0.75
+Barcode128AsImage= true
+LongDotDashedStyle= 6;2;1;2
+DEBUG= false
+SearchNewFontsOnce= true
+TopMargin= 0.75
+LongDashedStyle= 6;2
+DashedStyle= 4;2
+SearchNewFonts= false
+BottomMargin= 6
+AdjustToPaper= true
+MarginsInsideBorder= false
+LineCapProjectingSquare= true
+RunDirection= 2
+Version= 1.0.0.0
+
+[Fonts Location (MS)]
+Microsoft Sans Serif= C:\Windows\fonts\micross.ttf
+Microsoft Sans Serif,Bold= C:\Windows\fonts\micross.ttf
diff --git a/dotnet/test/DotNetCoreWebUnitTest/apps/apdfwebbasictest.cs b/dotnet/test/DotNetCoreWebUnitTest/apps/apdfwebbasictest.cs
new file mode 100644
index 000000000..6924bf049
--- /dev/null
+++ b/dotnet/test/DotNetCoreWebUnitTest/apps/apdfwebbasictest.cs
@@ -0,0 +1,196 @@
+/*
+ File: PDFBasicTest
+ Description: PDFBasic Test
+ Author: GeneXus .NET Framework Generator version 17_0_8-156507
+ Generated on: 12/21/2021 16:34:13.62
+ Program type: Main program
+ Main DBMS: SQL Server
+*/
+using GeneXus.Application;
+using GeneXus.Data.NTier;
+using GeneXus.Http.Server;
+using GeneXus.Procedure;
+using GeneXus.Utils;
+
+namespace GeneXus.Programs
+{
+ public class apdfwebbasictest : GXWebProcedure
+ {
+
+ public apdfwebbasictest( )
+ {
+ context = new GxContext( );
+ DataStoreUtil.LoadDataStores( context);
+ IsMain = true;
+ context.SetDefaultTheme("Carmine");
+ }
+
+ public apdfwebbasictest( IGxContext context )
+ {
+ this.context = context;
+ IsMain = false;
+ }
+ public override void webExecute()
+ {
+ context.SetDefaultTheme("PDFReportTest", true);
+ GxContext gxcontext = context as GxContext;
+ gxcontext.SetPhysicalPath(System.IO.Directory.GetCurrentDirectory());
+ initialize();
+ executePrivate();
+ cleanup();
+ }
+
+ public void execute()
+ {
+ initialize();
+ executePrivate();
+ }
+ void executePrivate()
+ {
+ /* GeneXus formulas */
+ /* Output device settings */
+ M_top = 0;
+ M_bot = 6;
+ P_lines = (int)(66-M_bot);
+ getPrinter().GxClearAttris() ;
+ AddMetrics( ) ;
+ lineHeight = 15;
+ gxXPage = 100;
+ gxYPage = 100;
+ GxHttpResponse httpResponse = new GxHttpResponse(context);
+ httpResponse.AppendHeader("Content-Disposition", "inline; filename=Report.pdf");
+ getPrinter().GxSetDocFormat("PDF") ;
+ try
+ {
+ Gx_out = "FIL" ;
+ if (!initPrinter (Gx_out, gxXPage, gxYPage, "GXPRN.INI", "", "", 2, 1, 256, 16834, 9504, 0, 1, 1, 0, 1, 1) )
+ {
+ cleanup();
+ return;
+ }
+ getPrinter().setModal(false) ;
+ P_lines = (int)(gxYPage-(lineHeight*6));
+ Gx_line = (int)(P_lines+1);
+ getPrinter().setPageLines(P_lines);
+ getPrinter().setLineHeight(lineHeight);
+ getPrinter().setM_top(M_top);
+ getPrinter().setM_bot(M_bot);
+ AV8htmlvar = "This is an example of a simple HTML page with one paragraph.
";
+ H1N0( false, 193) ;
+ getPrinter().GxAttris("Microsoft Sans Serif", 14, false, false, false, false, 0, 0, 0, 0, 0, 255, 255, 255) ;
+ getPrinter().GxDrawText("Item List", 17, Gx_line+33, 400, Gx_line+66, 0, 0, 0, 0) ;
+ getPrinter().GxDrawRect(33, Gx_line+33, 133, Gx_line+63, 1, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0) ;
+ getPrinter().GxAttris("Microsoft Sans Serif", 10, true, false, false, false, 0, 0, 0, 0, 0, 255, 255, 255) ;
+ getPrinter().GxDrawText("Code", 100, Gx_line+83, 140, Gx_line+101, 0+256, 0, 0, 0) ;
+ getPrinter().GxDrawText("Name", 183, Gx_line+83, 283, Gx_line+101, 0, 0, 0, 0) ;
+ getPrinter().GxDrawBitMap("lamp.png", 411, Gx_line+33, 619, Gx_line+241) ;
+ getPrinter().GxAttris("Microsoft Sans Serif", 8, false, false, false, false, 0, 0, 0, 0, 0, 255, 255, 255) ;
+ getPrinter().GxDrawText(StringUtil.RTrim( AV8htmlvar), 22, Gx_line+122, 400, Gx_line+178, 0, 1, 0, 0) ;
+ Gx_OldLine = Gx_line;
+ Gx_line = (int)(Gx_line+193);
+ /* Print footer for last page */
+ ToSkip = (int)(P_lines+1);
+ H1N0( true, 0) ;
+ }
+ catch ( GeneXus.Printer.ProcessInterruptedException )
+ {
+ }
+ finally
+ {
+ /* Close printer file */
+ try
+ {
+ getPrinter().GxEndPage() ;
+ getPrinter().GxEndDocument() ;
+ }
+ catch ( GeneXus.Printer.ProcessInterruptedException )
+ {
+ }
+ endPrinter();
+ }
+ this.cleanup();
+ }
+
+ protected void H1N0( bool bFoot ,
+ int Inc )
+ {
+ /* Skip the required number of lines */
+ while ( ( ToSkip > 0 ) || ( Gx_line + Inc > P_lines ) )
+ {
+ if ( Gx_line + Inc >= P_lines )
+ {
+ if ( Gx_page > 0 )
+ {
+ /* Print footers */
+ Gx_line = P_lines;
+ getPrinter().GxEndPage() ;
+ if ( bFoot )
+ {
+ return ;
+ }
+ }
+ ToSkip = 0;
+ Gx_line = 0;
+ Gx_page = (int)(Gx_page+1);
+ /* Skip Margin Top Lines */
+ Gx_line = (int)(Gx_line+(M_top*lineHeight));
+ /* Print headers */
+ getPrinter().GxStartPage() ;
+ if (true) break;
+ }
+ else
+ {
+ Gx_line = (int)(Gx_line+1);
+ }
+ ToSkip = (int)(ToSkip-1);
+ }
+ getPrinter().setPage(Gx_page);
+ }
+
+ protected void AddMetrics( )
+ {
+ AddMetrics0( ) ;
+ AddMetrics1( ) ;
+ }
+
+ protected void AddMetrics0( )
+ {
+ getPrinter().setMetrics("Microsoft Sans Serif", false, false, 58, 14, 72, 171, new int[] {48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 18, 20, 23, 36, 36, 57, 43, 12, 21, 21, 25, 37, 18, 21, 18, 18, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 18, 18, 37, 37, 37, 36, 65, 43, 43, 46, 46, 43, 39, 50, 46, 18, 32, 43, 36, 53, 46, 50, 43, 50, 46, 43, 40, 46, 43, 64, 41, 42, 39, 18, 18, 18, 27, 36, 21, 36, 36, 32, 36, 36, 18, 36, 36, 14, 15, 33, 14, 55, 36, 36, 36, 36, 21, 32, 18, 36, 33, 47, 31, 31, 31, 21, 17, 21, 37, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 18, 20, 36, 36, 36, 36, 17, 36, 21, 47, 24, 36, 37, 21, 47, 35, 26, 35, 21, 21, 21, 37, 34, 21, 21, 21, 23, 36, 53, 53, 53, 39, 43, 43, 43, 43, 43, 43, 64, 46, 43, 43, 43, 43, 18, 18, 18, 18, 46, 46, 50, 50, 50, 50, 50, 37, 50, 46, 46, 46, 46, 43, 43, 39, 36, 36, 36, 36, 36, 36, 57, 32, 36, 36, 36, 36, 18, 18, 18, 18, 36, 36, 36, 36, 36, 36, 36, 35, 39, 36, 36, 36, 36, 32, 36, 32}) ;
+ }
+
+ protected void AddMetrics1( )
+ {
+ getPrinter().setMetrics("Microsoft Sans Serif", true, false, 57, 15, 72, 163, new int[] {47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 17, 19, 29, 34, 34, 55, 45, 15, 21, 21, 24, 36, 17, 21, 17, 17, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 21, 36, 36, 36, 38, 60, 43, 45, 45, 45, 41, 38, 48, 45, 17, 34, 45, 38, 53, 45, 48, 41, 48, 45, 41, 38, 45, 41, 57, 41, 41, 38, 21, 17, 21, 36, 34, 21, 34, 38, 34, 38, 34, 21, 38, 38, 17, 17, 34, 17, 55, 38, 38, 38, 38, 24, 34, 21, 38, 33, 49, 34, 34, 31, 24, 17, 24, 36, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 17, 21, 34, 34, 34, 34, 17, 34, 21, 46, 23, 34, 36, 21, 46, 34, 25, 34, 21, 21, 21, 36, 34, 21, 20, 21, 23, 34, 52, 52, 52, 38, 45, 45, 45, 45, 45, 45, 62, 45, 41, 41, 41, 41, 17, 17, 17, 17, 45, 45, 48, 48, 48, 48, 48, 36, 48, 45, 45, 45, 45, 41, 41, 38, 34, 34, 34, 34, 34, 34, 55, 34, 34, 34, 34, 34, 17, 17, 17, 17, 38, 38, 38, 38, 38, 38, 38, 34, 38, 38, 38, 38, 38, 34, 38, 34}) ;
+ }
+
+ public override int getOutputType( )
+ {
+ return GxReportUtils.OUTPUT_PDF ;
+ }
+
+ public override void cleanup( )
+ {
+ if (IsMain) waitPrinterEnd();
+ if ( IsMain )
+ {
+ context.CloseConnections();
+ }
+ ExitApp();
+ }
+
+ public override void initialize( )
+ {
+ AV8htmlvar = "";
+ /* GeneXus formulas. */
+ Gx_line = 0;
+ context.Gx_err = 0;
+ }
+
+ private int M_top ;
+ private int M_bot ;
+ private int ToSkip ;
+ private int Gx_OldLine ;
+ private string AV8htmlvar ;
+ }
+
+}
diff --git a/dotnet/test/DotNetCoreWebUnitTest/lamp.png b/dotnet/test/DotNetCoreWebUnitTest/lamp.png
new file mode 100644
index 000000000..840d5d514
Binary files /dev/null and b/dotnet/test/DotNetCoreWebUnitTest/lamp.png differ