Skip to content

Commit

Permalink
Initial support for Pdf reports in PDF/A 1A and 1B (#762)
Browse files Browse the repository at this point in the history
* Initial support for PDF/A 1a and 1b for .NET and .NET Framework reports.

* Initialize default complianceLevel with None (default used until now).

* Do not check report name in test until while #761 is open.

* Set sRGB Color Space Profile  for PDFA_1B.

* Turn off test of PDFA. It seems to cause a timeout when running on github actions.
  • Loading branch information
claudiamurialdo authored Jul 27, 2023
1 parent a9c0a9d commit 1638203
Show file tree
Hide file tree
Showing 10 changed files with 398 additions and 9 deletions.
4 changes: 4 additions & 0 deletions dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
<Compile Include="..\..\dotnetframework\GxPdfReportsCS\PDFReportItext.cs" Link="PDFReportItext.cs" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="..\..\dotnetframework\GxPdfReportsCS\sRGB Color Space Profile.icm" Link="sRGB Color Space Profile.icm" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="iTextSharp-LGPL" Version="4.1.6" />
<PackageReference Include="log4net" Version="2.0.15" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("DotNetCoreWebUnitTest")]
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
<PackageTags>Itext PDF Report</PackageTags>
<PackageId>GeneXus.PdfReportsCS</PackageId>
</PropertyGroup>
<ItemGroup>
<None Remove="sRGB Color Space Profile.icm" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="sRGB Color Space Profile.icm" />
</ItemGroup>


<ItemGroup>
Expand Down
101 changes: 92 additions & 9 deletions dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using GeneXus.Utils;
using System.Reflection;
using GeneXus.Metadata;
using GeneXus.Configuration;

namespace com.genexus.reports
{
Expand All @@ -45,6 +46,7 @@ public class PDFReportItextSharp : IReportHandler
private bool fontUnderline;
private bool fontStrikethru;
private int fontSize;
private string language;

//Color for, BaseColor for => Itext5
private object backColor, foreColor, templateColorFill;
Expand Down Expand Up @@ -89,6 +91,7 @@ public class PDFReportItextSharp : IReportHandler
public static float DOTS_UNITS_ON = 1;
public bool lineCapProjectingSquare = true;
public bool barcode128AsImage = true;
private PdfConformanceLevel complianceLevel = PdfConformanceLevel.None;
internal Dictionary<string, Image> documentImages;
float[] STYLE_SOLID = new float[] { 1, 0 };//0
float[] STYLE_NONE = null;//1
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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();


Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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
}
}

Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@


<ItemGroup>
<PackageReference Include="Codeuctivity.PdfAValidator" Version="2.3.104" />
<PackageReference Include="FreeSpire.PDF" Version="8.6.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="6.0.5" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="1.1.4" PrivateAssets="All" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.5.1" />
Expand All @@ -34,6 +36,7 @@
<ProjectReference Include="..\..\src\dotnetcore\GxClasses.Web\GxClasses.Web.csproj" />
<ProjectReference Include="..\..\src\dotnetcore\GxClasses\GxClasses.csproj" />
<ProjectReference Include="..\..\src\dotnetcore\GxNetCoreStartup\GxNetCoreStartup.csproj" />
<ProjectReference Include="..\..\src\dotnetcore\GxPdfReportsCS\GxPdfReportsCS.csproj" />
<ProjectReference Include="..\..\src\dotnetcore\Providers\Storage\GXAmazonS3\GXAmazonS3.csproj" />
</ItemGroup>

Expand Down Expand Up @@ -67,6 +70,12 @@
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="lamp.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="PDFReport.ini">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="uruguay.flag.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
61 changes: 61 additions & 0 deletions dotnet/test/DotNetCoreWebUnitTest/Middleware/WebReportTest.cs
Original file line number Diff line number Diff line change
@@ -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);



}

}

}
27 changes: 27 additions & 0 deletions dotnet/test/DotNetCoreWebUnitTest/PDFReport.ini
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 1638203

Please sign in to comment.