diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props index 54923a4af..03e14d90e 100644 --- a/dotnet/Directory.Build.props +++ b/dotnet/Directory.Build.props @@ -3,7 +3,7 @@ 11.0.0.0 1 $([MSBuild]::Add($(MajorFileVersion), 100)) - 26 + 27 $(COMMIT_NUMBER) 0 $(MajorFileVersion).$(MinorFileVersion).$(PatchFileVersion) diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln index 00009ab39..cab5bac35 100644 --- a/dotnet/DotNetStandardClasses.sln +++ b/dotnet/DotNetStandardClasses.sln @@ -249,6 +249,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXAzureEventGrid", "src\dot EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreAttackMitigationTest", "test\DotNetCoreAttackMitigationTest\DotNetCoreAttackMitigationTest.csproj", "{2D615969-53E2-4B77-9A9A-75C33865CF76}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetPDFUnitTest", "test\DotNetPdfTest\DotNetPDFUnitTest.csproj", "{0FCFB078-5584-469F-92CC-61B0A6216D0D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -603,6 +605,10 @@ Global {2D615969-53E2-4B77-9A9A-75C33865CF76}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D615969-53E2-4B77-9A9A-75C33865CF76}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D615969-53E2-4B77-9A9A-75C33865CF76}.Release|Any CPU.Build.0 = Release|Any CPU + {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -722,6 +728,7 @@ Global {5BBC75F0-E51A-4EBD-A628-92498D319B1D} = {4C43F2DA-59E5-46F5-B691-195449498555} {7250CDB1-95C4-4822-B01B-3CBD73324CC9} = {30159B0F-BE61-4DB7-AC02-02851426BE4B} {2D615969-53E2-4B77-9A9A-75C33865CF76} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {0FCFB078-5584-469F-92CC-61B0A6216D0D} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C} diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs new file mode 100644 index 000000000..4005ca78a --- /dev/null +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs @@ -0,0 +1,65 @@ + + +using System; +using System.Threading.Tasks; +using GeneXus.Configuration; +using GeneXus.Http; +using log4net; +using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.Http; +namespace GeneXus.Application +{ + public class ValidateAntiForgeryTokenMiddleware + { + static readonly ILog log = log4net.LogManager.GetLogger(typeof(ValidateAntiForgeryTokenMiddleware)); + + private readonly RequestDelegate _next; + private readonly IAntiforgery _antiforgery; + private string _basePath; + + public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery, String basePath) + { + _next = next; + _antiforgery = antiforgery; + _basePath = "/" + basePath; + } + + public async Task Invoke(HttpContext context) + { + if (context.Request.Path.HasValue && context.Request.Path.Value.StartsWith(_basePath)) + { + if (HttpMethods.IsPost(context.Request.Method) || + HttpMethods.IsDelete(context.Request.Method) || + HttpMethods.IsPut(context.Request.Method)) + { + string cookieToken = context.Request.Cookies[HttpHeader.X_CSRF_TOKEN_COOKIE]; + string headerToken = context.Request.Headers[HttpHeader.X_CSRF_TOKEN_HEADER]; + GXLogging.Debug(log, $"Antiforgery validation, cookieToken:{cookieToken}, headerToken:{headerToken}"); + + await _antiforgery.ValidateRequestAsync(context); + GXLogging.Debug(log, $"Antiforgery validation OK"); + } + else if (HttpMethods.IsGet(context.Request.Method)) + { + SetAntiForgeryTokens(_antiforgery, context); + } + } + if (!context.Request.Path.Value.EndsWith(_basePath)) //VerificationToken + await _next(context); + } + internal static void SetAntiForgeryTokens(IAntiforgery _antiforgery, HttpContext context) + { + AntiforgeryTokenSet tokenSet = _antiforgery.GetAndStoreTokens(context); + string sameSite; + CookieOptions cookieOptions = new CookieOptions { HttpOnly = false, Secure = GxContext.GetHttpSecure(context) == 1 }; + SameSiteMode sameSiteMode = SameSiteMode.Unspecified; + if (Config.GetValueOf("SAMESITE_COOKIE", out sameSite) && Enum.TryParse(sameSite, out sameSiteMode)) + { + cookieOptions.SameSite = sameSiteMode; + } + context.Response.Cookies.Append(HttpHeader.X_CSRF_TOKEN_COOKIE, tokenSet.RequestToken, cookieOptions); + GXLogging.Debug(log, $"Setting cookie ", HttpHeader.X_CSRF_TOKEN_COOKIE, "=", tokenSet.RequestToken, " samesite:" + sameSiteMode); + } + + } +} diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj index c0d240431..5f110bf8f 100644 --- a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj @@ -17,6 +17,16 @@ + + + + + + + + + + diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs index e30a95c41..80e5e5a44 100644 --- a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs @@ -1,10 +1,10 @@ - - using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Threading.Tasks; +using Azure.Identity; +using Azure.Monitor.OpenTelemetry.Exporter; using GeneXus.Configuration; using GeneXus.Http; using GeneXus.HttpHandlerFactory; @@ -30,6 +30,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; +using OpenTelemetry.Logs; +using OpenTelemetry.Resources; using StackExchange.Redis; @@ -39,6 +41,10 @@ public class Program { const string DEFAULT_PORT = "80"; static string DEFAULT_SCHEMA = Uri.UriSchemeHttp; + + private static string OPENTELEMETRY_SERVICE = "Observability"; + private static string OPENTELEMETRY_AZURE_DISTRO = "GeneXus.OpenTelemetry.Azure.AzureAppInsights"; + private static string APPLICATIONINSIGHTS_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING"; public static void Main(string[] args) { try @@ -76,13 +82,12 @@ public static void Main(string[] args) } public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .ConfigureLogging(logging => logging.AddConsole()) - .UseStartup() - .UseWebRoot(Startup.LocalPath) - .UseContentRoot(Startup.LocalPath) - .Build(); - + WebHost.CreateDefaultBuilder(args) + .ConfigureLogging(WebHostConfigureLogging) + .UseStartup() + .UseContentRoot(Startup.LocalPath) + .Build(); + public static IWebHost BuildWebHostPort(string[] args, string port) { return BuildWebHostPort(args, port, DEFAULT_SCHEMA); @@ -90,13 +95,47 @@ public static IWebHost BuildWebHostPort(string[] args, string port) static IWebHost BuildWebHostPort(string[] args, string port, string schema) { return WebHost.CreateDefaultBuilder(args) - .ConfigureLogging(logging => logging.AddConsole()) + .ConfigureLogging(WebHostConfigureLogging) .UseUrls($"{schema}://*:{port}") .UseStartup() .UseWebRoot(Startup.LocalPath) .UseContentRoot(Startup.LocalPath) .Build(); } + + private static void WebHostConfigureLogging(WebHostBuilderContext hostingContext, ILoggingBuilder loggingBuilder) + { + loggingBuilder.AddConsole(); + GXService providerService = GXServices.Instance?.Get(OPENTELEMETRY_SERVICE); + if (providerService != null && providerService.ClassName.StartsWith(OPENTELEMETRY_AZURE_DISTRO)) + { + ConfigureAzureOpentelemetry(loggingBuilder); + } + } + private static void ConfigureAzureOpentelemetry(ILoggingBuilder loggingBuilder) + { + string endpoint = Environment.GetEnvironmentVariable(APPLICATIONINSIGHTS_CONNECTION_STRING); + var resourceBuilder = ResourceBuilder.CreateDefault() + .AddTelemetrySdk(); + + loggingBuilder.AddOpenTelemetry(loggerOptions => + { + loggerOptions + .SetResourceBuilder(resourceBuilder) + .AddAzureMonitorLogExporter(options => + { + if (!string.IsNullOrEmpty(endpoint)) + options.ConnectionString = endpoint; + else + options.Credential = new DefaultAzureCredential(); + }) + .AddConsoleExporter(); + + loggerOptions.IncludeFormattedMessage = true; + loggerOptions.IncludeScopes = true; + loggerOptions.ParseStateValues = true; + }); + } private static void LocatePhysicalLocalPath() { string startup = FileUtil.GetStartupDirectory(); @@ -212,7 +251,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddAntiforgery(options => { - options.HeaderName = HttpHeader.X_GXCSRF_TOKEN; + options.HeaderName = HttpHeader.X_CSRF_TOKEN_HEADER; options.SuppressXFrameOptionsHeader = false; }); } @@ -419,20 +458,10 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos routes.MapRoute($"{s}", new RequestDelegate(gxRouting.ProcessRestRequest)); } } - routes.MapRoute($"{restBasePath}VerificationToken", (context) => - { - string requestPath = context.Request.Path.Value; - - if (string.Equals(requestPath, $"/{restBasePath}VerificationToken", StringComparison.OrdinalIgnoreCase) && antiforgery!=null) - { - ValidateAntiForgeryTokenMiddleware.SetAntiForgeryTokens(antiforgery, context); - } - return Task.CompletedTask; - }); routes.MapRoute($"{restBasePath}{{*{UrlTemplateControllerWithParms}}}", new RequestDelegate(gxRouting.ProcessRestRequest)); routes.MapRoute("Default", VirtualPath, new { controller = "Home", action = "Index" }); }); - + app.UseWebSockets(); string basePath = string.IsNullOrEmpty(VirtualPath) ? string.Empty : $"/{VirtualPath}"; Config.ScriptPath = basePath; @@ -589,54 +618,4 @@ public IActionResult Index() return Redirect(defaultFiles[0]); } } - public class ValidateAntiForgeryTokenMiddleware - { - static readonly ILog log = log4net.LogManager.GetLogger(typeof(ValidateAntiForgeryTokenMiddleware)); - - private readonly RequestDelegate _next; - private readonly IAntiforgery _antiforgery; - private string _basePath; - - public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery, String basePath) - { - _next = next; - _antiforgery = antiforgery; - _basePath = "/" + basePath; - } - - public async Task Invoke(HttpContext context) - { - if (context.Request.Path.HasValue && context.Request.Path.Value.StartsWith(_basePath)) - { - if (HttpMethods.IsPost(context.Request.Method) || - HttpMethods.IsDelete(context.Request.Method) || - HttpMethods.IsPut(context.Request.Method)) - { - string cookieToken = context.Request.Cookies[HttpHeader.X_GXCSRF_TOKEN]; - string headerToken = context.Request.Headers[HttpHeader.X_GXCSRF_TOKEN]; - GXLogging.Debug(log, $"Antiforgery validation, cookieToken:{cookieToken}, headerToken:{headerToken}"); - - await _antiforgery.ValidateRequestAsync(context); - GXLogging.Debug(log, $"Antiforgery validation OK"); - } - else if (HttpMethods.IsGet(context.Request.Method)) - { - string tokens = context.Request.Cookies[HttpHeader.X_GXCSRF_TOKEN]; - if (string.IsNullOrEmpty(tokens)) - { - SetAntiForgeryTokens(_antiforgery, context); - } - } - } - await _next(context); - } - internal static void SetAntiForgeryTokens(IAntiforgery _antiforgery, HttpContext context) - { - AntiforgeryTokenSet tokenSet = _antiforgery.GetAndStoreTokens(context); - context.Response.Cookies.Append(HttpHeader.X_GXCSRF_TOKEN, tokenSet.RequestToken, - new CookieOptions { HttpOnly = false, Secure = GxContext.GetHttpSecure(context) == 1 }); - GXLogging.Debug(log, $"Setting cookie ", HttpHeader.X_GXCSRF_TOKEN, "=", tokenSet.RequestToken); - } - - } } diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs index 37a5076e3..dc00e11aa 100644 --- a/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs @@ -16,3 +16,5 @@ [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.NativeSharpFunctionsMS.getRegistrySubValues(System.String,System.String)~System.Collections.ArrayList")] [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.NativeSharpFunctionsMS.ReadRegKey(System.String)~System.String")] [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.MSPDFFontDescriptor.getTrueTypeFontLocation(System.String)~System.String")] +[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportItextBase.getAcrobatLocation~System.String")] +[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportItextBase.loadSubstituteTable")] diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj b/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj index 016d52c2f..be3de3a6a 100644 --- a/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj @@ -9,15 +9,19 @@ - + + - - + + + + + all diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs new file mode 100644 index 000000000..6c2918fce --- /dev/null +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs @@ -0,0 +1,1332 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using GeneXus; +using iText.Barcodes; +using iText.Html2pdf; +using iText.Html2pdf.Resolver.Font; +using iText.IO.Font; +using iText.IO.Font.Otf; +using iText.IO.Image; +using iText.Kernel.Colors; +using iText.Kernel.Exceptions; +using iText.Kernel.Font; +using iText.Kernel.Geom; +using iText.Kernel.Pdf; +using iText.Kernel.Pdf.Action; +using iText.Kernel.Pdf.Canvas; +using iText.Layout; +using iText.Layout.Borders; +using iText.Layout.Element; +using iText.Layout.Font; +using iText.Layout.Layout; +using iText.Layout.Properties; +using iText.Layout.Renderer; +using iText.Layout.Splitting; +using log4net; +using Path = System.IO.Path; + +namespace GeneXus.Printer +{ + public class GxReportBuilderPdf8 : GxReportBuilderPdf + { + static ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + public GxReportBuilderPdf8() { } + public GxReportBuilderPdf8(string appPath, Stream outputStream) + { + + _pdfReport = new com.genexus.reports.PDFReportItext8(appPath); + if (outputStream != null) + { + _pdfReport.setOutputStream(outputStream); + GXLogging.Debug(log, "GxReportBuilderPdf outputStream: binaryWriter"); + } + } + } + +} +namespace com.genexus.reports +{ + + public class PDFReportItext8 : PDFReportItextBase + { + + static ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + //const int ASCENT_NORMALIZED_UNITS = 1000; + + private PageSize pageSize; + private PdfFont baseFont; + private Style fontStyle; + //Color for, BaseColor for => Itext5 + private Color backColor, foreColor, templateColorFill; + private Document document; + PdfDocument pdfDocument; + private PdfPage pdfPage; + private PdfWriter writer; + private Rectangle templateRectangle; + private float templatex, templatey; + private PdfFont templateFont; + private PdfFont defaultFont; + internal Dictionary documentImages; + Barcode128 barcode; + private Boolean fontBold; + private Boolean fontItalic; + + public PDFReportItext8(String appPath) : base(appPath) + { + documentImages = new Dictionary(); + } + + protected override void init(ref int gxYPage, ref int gxXPage, int pageWidth, int pageLength) + { + this.pageSize = ComputePageSize(leftMargin, topMargin, pageWidth, pageLength, props.getBooleanGeneralProperty(Const.MARGINS_INSIDE_BORDER, Const.DEFAULT_MARGINS_INSIDE_BORDER)); + gxXPage = (int)this.pageSize.GetRight(); + if (props.getBooleanGeneralProperty(Const.FIX_SAC24437, true)) + gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y); + else + gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y_OLD); + + writer = new PdfWriter(outputStream); + writer.SetCompressionLevel(CompressionConstants.BEST_COMPRESSION); + try + { + string level = props.getGeneralProperty(Const.COMPLIANCE_LEVEL); + if (Enum.TryParse(level, true, out complianceLevel)) + { + //if (SetComplainceLevel(complianceLevel)) + //writer.SetTagged(); + } + pdfDocument = new PdfDocument(writer); + pdfDocument.SetDefaultPageSize(this.pageSize); + document = new Document(pdfDocument); + document.SetFontProvider(new DefaultFontProvider(false, false, false)); + } + catch (PdfException de) + { + GXLogging.Debug(log, "GxDrawRect error", de); + } + } + + internal override 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; + }*/ + return false; + } + + /** + * @param hideCorners indicates whether corner triangles should be hidden when the side that joins them is hidden. + */ + + private void drawRectangle(PdfCanvas cb, float x, float y, float w, float h, int styleTop, int styleBottom, int styleRight, int styleLeft, + float radioTL, float radioTR, float radioBL, float radioBR, float penAux, bool hideCorners) + { + + float[] dashPatternTop = getDashedPattern(styleTop); + float[] dashPatternBottom = getDashedPattern(styleBottom); + float[] dashPatternLeft = getDashedPattern(styleLeft); + float[] dashPatternRight = getDashedPattern(styleRight); + + //-------------------bottom line--------------------- + if (styleBottom != STYLE_NONE_CONST) + { + cb.SetLineDash(dashPatternBottom, 0); + } + + float b = 0.4477f; + if (radioBL > 0) + { + cb.MoveTo(x + radioBL, y); + } + else + { + if (hideCorners && styleLeft == STYLE_NONE_CONST && radioBL == 0) + { + cb.MoveTo(x + penAux, y); + } + else + { + cb.MoveTo(x, y); + } + } + + //-------------------bottom right corner--------------------- + + if (styleBottom != STYLE_NONE_CONST) + { + if (hideCorners && styleRight == STYLE_NONE_CONST && radioBR == 0) + { + cb.LineTo(x + w - penAux, y); + } + else + { + cb.LineTo(x + w - radioBR, y); + } + if (radioBR > 0 && styleRight != STYLE_NONE_CONST) + { + cb.CurveTo(x + w - radioBR * b, y, x + w, y + radioBR * b, x + w, y + radioBR); + } + } + + //-------------------right line--------------------- + + if (styleRight != STYLE_NONE_CONST && dashPatternRight != dashPatternBottom) + { + cb.Stroke(); + cb.SetLineDash(dashPatternRight, 0); + if (hideCorners && styleBottom == STYLE_NONE_CONST && radioBR == 0) + { + cb.MoveTo(x + w, y + penAux); + } + else + { + cb.MoveTo(x + w, y + radioBR); + } + } + + //-------------------top right corner--------------------- + if (styleRight != STYLE_NONE_CONST) + { + if (hideCorners && styleTop == STYLE_NONE_CONST && radioTR == 0) + { + cb.LineTo(x + w, y + h - penAux); + } + else + { + cb.LineTo(x + w, y + h - radioTR); + } + if (radioTR > 0 && styleTop != STYLE_NONE_CONST) + { + cb.CurveTo(x + w, y + h - radioTR * b, x + w - radioTR * b, y + h, x + w - radioTR, y + h); + } + } + + //-------------------top line--------------------- + + if (styleTop != STYLE_NONE_CONST && dashPatternTop != dashPatternRight) + { + cb.Stroke(); + cb.SetLineDash(dashPatternTop, 0); + if (hideCorners && styleRight == STYLE_NONE_CONST && radioTR == 0) + { + cb.MoveTo(x + w - penAux, y + h); + } + else + { + cb.MoveTo(x + w - radioTR, y + h); + } + } + + //-------------------top left corner--------------------- + if (styleTop != STYLE_NONE_CONST) + { + if (hideCorners && styleLeft == STYLE_NONE_CONST && radioTL == 0) + { + cb.LineTo(x + penAux, y + h); + } + else + { + cb.LineTo(x + radioTL, y + h); + } + if (radioTL > 0 && styleLeft != STYLE_NONE_CONST) + { + cb.CurveTo(x + radioTL * b, y + h, x, y + h - radioTL * b, x, y + h - radioTL); + } + } + + //-------------------left line--------------------- + + if (styleLeft != STYLE_NONE_CONST && dashPatternLeft != dashPatternTop) + { + cb.Stroke(); + cb.SetLineDash(dashPatternLeft, 0); + if (hideCorners && styleTop == STYLE_NONE_CONST && radioTL == 0) + { + cb.MoveTo(x, y + h - penAux); + } + else + { + cb.MoveTo(x, y + h - radioTL); + } + } + + //-------------------bottom left corner--------------------- + if (styleLeft != STYLE_NONE_CONST) + { + if (hideCorners && styleBottom == STYLE_NONE_CONST && radioBL == 0) + { + cb.LineTo(x, y + penAux); + } + else + { + cb.LineTo(x, y + radioBL); + } + if (radioBL > 0 && styleBottom != STYLE_NONE_CONST) + { + cb.CurveTo(x, y + radioBL * b, x + radioBL * b, y, x + radioBL, y); + } + } + cb.Stroke(); + + } + + private void roundRectangle(PdfCanvas cb, float x, float y, float w, float h, float radioTL, float radioTR, float radioBL, float radioBR) + { + //-------------------bottom line--------------------- + + + float b = 0.4477f; + if (radioBL > 0) + { + cb.MoveTo(x + radioBL, y); + } + else + { + cb.MoveTo(x, y); + } + + //-------------------bottom right corner--------------------- + + cb.LineTo(x + w - radioBR, y); + if (radioBR > 0) + { + cb.CurveTo(x + w - radioBR * b, y, x + w, y + radioBR * b, x + w, y + radioBR); + } + + + cb.LineTo(x + w, y + h - radioTR); + if (radioTR > 0) + { + cb.CurveTo(x + w, y + h - radioTR * b, x + w - radioTR * b, y + h, x + w - radioTR, y + h); + } + + cb.LineTo(x + radioTL, y + h); + if (radioTL > 0) + { + cb.CurveTo(x + radioTL * b, y + h, x, y + h - radioTL * b, x, y + h - radioTL); + } + cb.LineTo(x, y + radioBL); + if (radioBL > 0) + { + cb.CurveTo(x, y + radioBL * b, x + radioBL * b, y, x + radioBL, y); + } + } + + public override void GxDrawRect(int left, int top, int right, int bottom, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue, + int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) + { + Color rectBackColor = new DeviceRgb(backRed, backGreen, backBlue); + Color rectForeColor = new DeviceRgb(foreRed, foreGreen, foreBlue); + GxDrawRect(left, top, right, bottom, pen, rectForeColor, backMode, rectBackColor, styleTop, styleBottom, styleRight, styleLeft, cornerRadioTL, cornerRadioTR, cornerRadioBL, cornerRadioBR); + } + + void GxDrawRect(int left, int top, int right, int bottom, int pen, Color foreColor, int backMode, Color backColor, + int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) + { + PdfCanvas cb = new PdfCanvas(pdfPage); + + float penAux = (float)convertScale(pen); + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + GXLogging.Debug(log, "GxDrawRect -> (" + left + "," + top + ") - (" + right + "," + bottom + ") BackMode: " + backMode + " Pen:" + pen + ",leftMargin:" + leftMargin); + cb.SaveState(); + + float x1, y1, x2, y2; + x1 = leftAux + leftMargin; + y1 = pageSize.GetTop() - bottomAux - topMargin - bottomMargin; + x2 = rightAux + leftMargin; + y2 = pageSize.GetTop() - topAux - topMargin - bottomMargin; + + cb.SetLineWidth(penAux); + cb.SetLineCapStyle(PdfCanvasConstants.LineCapStyle.PROJECTING_SQUARE); + + if (cornerRadioBL == 0 && cornerRadioBR == 0 && cornerRadioTL == 0 && cornerRadioTR == 0 && styleBottom == 0 && styleLeft == 0 && styleRight == 0 && styleTop == 0) + { + //border color must be the same as the fill if border=0 since setLineWidth does not work. + if (pen > 0) + cb.SetStrokeColor(foreColor); + else + cb.SetStrokeColor(backColor); + cb.Rectangle(x1, y1, x2 - x1, y2 - y1); + + if (backMode != 0) + { + cb.SetFillColor(backColor); + cb.FillStroke(); + } + + cb.ClosePathStroke(); + } + else + { + float w = x2 - x1; + float h = y2 - y1; + if (w < 0) + { + x1 += w; + w = -w; + } + if (h < 0) + { + y1 += h; + h = -h; + } + + float cRadioTL = (float)convertScale(cornerRadioTL); + float cRadioTR = (float)convertScale(cornerRadioTR); + float cRadioBL = (float)convertScale(cornerRadioBL); + float cRadioBR = (float)convertScale(cornerRadioBR); + + // Scale the radius if it's too large or to small to fit. + int max = (int)Math.Min(w, h); + cRadioTL = Math.Max(0, Math.Min(cRadioTL, max / 2)); + cRadioTR = Math.Max(0, Math.Min(cRadioTR, max / 2)); + cRadioBL = Math.Max(0, Math.Min(cRadioBL, max / 2)); + cRadioBR = Math.Max(0, Math.Min(cRadioBR, max / 2)); + + if (backMode != 0) + { + cb.SetStrokeColor(backColor); + cb.SetLineWidth(0); + roundRectangle(cb, x1, y1, w, h, cRadioTL, cRadioTR, cRadioBL, cRadioBR); + cb.SetFillColor(backColor); + cb.FillStroke(); + cb.SetLineWidth(penAux); + } + if (pen > 0) + { + //Rectangle edge + cb.SetFillColor(backColor); + cb.SetStrokeColor(foreColor); + drawRectangle(cb, x1, y1, w, h, + styleTop, styleBottom, styleRight, styleLeft, + cRadioTL, cRadioTR, + cRadioBL, cRadioBR, penAux, false); + } + } + cb.RestoreState(); + } + public override void GxDrawLine(int left, int top, int right, int bottom, int width, int foreRed, int foreGreen, int foreBlue, int style) + { + PdfCanvas cb = new PdfCanvas(pdfPage); + + Color foreColor = new DeviceRgb(foreRed, foreGreen, foreBlue); + + float widthAux = (float)convertScale(width); + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + GXLogging.Debug(log, "GxDrawLine leftAux: " + leftAux + ",leftMargin:" + leftMargin + ",pageSize.Top:" + pageSize.GetTop() + ",bottomAux:" + bottomAux + ",topMargin:" + topMargin + ",bottomMargin:" + bottomMargin); + + GXLogging.Debug(log, "GxDrawLine -> (" + left + "," + top + ") - (" + right + "," + bottom + ") Width: " + width); + float x1, y1, x2, y2; + x1 = leftAux + leftMargin; + y1 = pageSize.GetTop() - bottomAux - topMargin - bottomMargin; + x2 = rightAux + leftMargin; + y2 = pageSize.GetTop() - topAux - topMargin - bottomMargin; + + GXLogging.Debug(log, "Line-> (" + (x1) + "," + y1 + ") - (" + x2 + "," + y2 + ") "); + cb.SaveState(); + cb.SetStrokeColor(foreColor); + cb.SetLineWidth(widthAux); + + if (lineCapProjectingSquare) + { + cb.SetLineCapStyle(PdfCanvasConstants.LineCapStyle.PROJECTING_SQUARE); + } + if (style != 0) + { + float[] dashPattern = getDashedPattern(style); + cb.SetLineDash(dashPattern, 0); + } + + cb.MoveTo(x1, y1); + cb.LineTo(x2, y2); + cb.Stroke(); + + cb.RestoreState(); + } + + public override void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom, int aspectRatio) + { + try + { + Image image; + Image imageRef; + if (documentImages != null && documentImages.TryGetValue(bitmap, out imageRef)) + { + image = imageRef; + } + else + { + try + { + if (!Path.IsPathRooted(bitmap)) + { + + image = new Image(ImageDataFactory.Create(defaultRelativePrepend + bitmap)); + if (image == null) + { + bitmap = webAppDir + bitmap; + image = new Image(ImageDataFactory.Create(bitmap)); + } + else + { + bitmap = defaultRelativePrepend + bitmap; + } + } + else + { + image = new Image(ImageDataFactory.Create(bitmap)); + } + } + catch (Exception)//absolute url + { + Uri uri = new Uri(bitmap); + image = new Image(ImageDataFactory.Create(uri)); + } + if (documentImages == null) + { + documentImages = new Dictionary(); + } + documentImages[bitmap] = image; + } + GXLogging.Debug(log, "GxDrawBitMap -> '" + bitmap + "' [" + left + "," + top + "] - Size: (" + (right - left) + "," + (bottom - top) + ")"); + + if (image != null) + { + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + image.SetFixedPosition(this.getPage(),leftAux + leftMargin, this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin); + if (aspectRatio == 0) + image.ScaleAbsolute(rightAux - leftAux, bottomAux - topAux); + else + image.ScaleToFit(rightAux - leftAux, bottomAux - topAux); + image.GetAccessibilityProperties().SetAlternateDescription(Path.GetFileName(bitmap)); + document.Add(image); + } + } + catch (PdfException de) + { + GXLogging.Error(log, "GxDrawBitMap document error", de); + } + catch (IOException ioe) + { + GXLogging.Error(log, "GxDrawBitMap io error", ioe); + } + catch (Exception e) + { + GXLogging.Error(log, "GxDrawBitMap error", e); + } + } + + public override void GxAttris(String fontName, int fontSize, bool fontBold, bool fontItalic, bool fontUnderline, bool fontStrikethru, int Pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue) + { + fontStyle = null; + bool _isCJK = false; + bool embeedFont = IsEmbeddedFont(fontName); + if (!embeedFont) + { + fontName = getSubstitute(fontName); + } + + GXLogging.Debug(log, "GxAttris: "); + GXLogging.Debug(log, "\\-> Font: " + fontName + " (" + fontSize + ")" + (fontBold ? " BOLD" : "") + (fontItalic ? " ITALIC" : "") + (fontStrikethru ? " Strike" : "")); + GXLogging.Debug(log, "\\-> Fore (" + foreRed + ", " + foreGreen + ", " + foreBlue + ")"); + GXLogging.Debug(log, "\\-> Back (" + backRed + ", " + backGreen + ", " + backBlue + ")"); + + if (barcode128AsImage && fontName.ToLower().IndexOf("barcode 128") >= 0 || fontName.ToLower().IndexOf("barcode128") >= 0) + { + barcode = new Barcode128(pdfDocument); + barcode.SetCodeType(Barcode128.CODE128); + } + else + { + barcode = null; + } + this.fontUnderline = fontUnderline; + this.fontStrikethru = fontStrikethru; + this.fontSize = fontSize; + this.fontBold = fontBold; + this.fontItalic = fontItalic; + foreColor = new DeviceRgb(foreRed, foreGreen, foreBlue); + backColor = new DeviceRgb(backRed, backGreen, backBlue); + + backFill = (backMode != 0); + try + { + //LoadAsianFontsDll(); + string f = fontName.ToLower(); + if (PDFFont.isType1(fontName)) + { + //Asian font + for (int i = 0; i < Type1FontMetrics.CJKNames.Length; i++) + { + if (Type1FontMetrics.CJKNames[i][0].ToLower().Equals(f) || + Type1FontMetrics.CJKNames[i][1].ToLower().Equals(f)) + { + fontStyle = new Style(); + if (fontItalic) fontStyle.SetItalic(); + if (fontBold) fontStyle.SetBold(); + + setAsianFont(fontName, string.Empty); + fontStyle.SetFont(baseFont); + + _isCJK = true; + break; + } + } + if (!_isCJK) + { + int style = 0; + if (fontBold && fontItalic) + style = style + 3; + else + { + if (fontItalic) + style = style + 2; + if (fontBold) + style = style + 1; + } + for (int i = 0; i < PDFFont.base14.Length; i++) + { + if (PDFFont.base14[i][0].ToLower().Equals(f)) + { + fontName = PDFFont.base14[i][1 + style].Substring(1); + break; + } + } + baseFont = PdfFontFactory.CreateFont(fontName, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + } + } + else + {//True type font + + if (IsEmbeddedFont(fontName)) + { + fontStyle = new Style(); + if (fontItalic) fontStyle.SetItalic(); + if (fontBold) fontStyle.SetBold(); + } + string fontPath = GetFontLocation(fontName); + bool foundFont = true; + if (string.IsNullOrEmpty(fontPath)) + { + MSPDFFontDescriptor fontDescriptor = new MSPDFFontDescriptor(); + fontPath = fontDescriptor.getTrueTypeFontLocation(fontName); + if (string.IsNullOrEmpty(fontPath)) + { + baseFont = CreateDefaultFont(); + foundFont = false; + } + else + { + props.setProperty(Const.MS_FONT_LOCATION, fontName, fontPath); + } + } + if (foundFont) + { + if (IsEmbeddedFont(fontName)) + { + try + { + baseFont = PdfFontFactory.CreateFont(fontPath, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); + GXLogging.Debug(log, "EMBEED_SECTION Font"); + } + catch (IOException ioEx) + { + Exception exDetailed = new Exception($"Error creating {fontPath}. Check font is installed for the current user", ioEx); + throw exDetailed; + } + } + else + { + + fontStyle = new Style(); + if (fontItalic) fontStyle.SetItalic(); + if (fontBold) fontStyle.SetBold(); + + GXLogging.Debug(log, "NOT EMBEED_SECTION Font"); + + baseFont = PdfFontFactory.CreateFont(fontPath, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + fontStyle.SetFont(baseFont); + } + } + else + { + GXLogging.Debug(log, "NOT foundFont fontName:" + fontName); + } + } + } + catch (PdfException de) + { + GXLogging.Debug(log, "GxAttris DocumentException", de); + throw de; + } + catch (Exception e) + { + GXLogging.Debug(log, "GxAttris error", e); + baseFont = CreateDefaultFont(); + } + } + + private PdfFont CreateDefaultFont() + { + if (defaultFont == null) + { + if (IsPdfA()) + defaultFont = PdfFontFactory.CreateFont("Helvetica", PdfEncodings.CP1252, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + else + defaultFont = PdfFontFactory.CreateFont("Helvetica", PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + } + return PdfFontFactory.CreateFont("Helvetica", PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + } + + public override void setAsianFont(String fontName, String style) + { + //LoadAsianFontsDll(); + try + { + if (fontName.Equals("Japanese")) + baseFont = PdfFontFactory.CreateFont("HeiseiMin-W3", "UniJIS-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + if (fontName.Equals("Japanese2")) + baseFont = PdfFontFactory.CreateFont("HeiseiKakuGo-W5", "UniJIS-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + if (fontName.Equals("SimplifiedChinese")) + baseFont = PdfFontFactory.CreateFont("STSong-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + if (fontName.Equals("TraditionalChinese")) + baseFont = PdfFontFactory.CreateFont("MHei-Medium", "UniCNS-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + if (fontName.Equals("Korean")) + baseFont = PdfFontFactory.CreateFont("HYSMyeongJo-Medium", "UniKS-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED); + } + catch (PdfException de) + { + GXLogging.Debug(log, "setAsianFont error", de); + } + catch (IOException ioe) + { + GXLogging.Debug(log, "setAsianFont io error", ioe); + } + } + + public override void GxDrawText(String sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign) + { + GXLogging.Debug(log, "GxDrawText, text:" + sTxt); + + bool printRectangle = false; + if (props.getBooleanGeneralProperty(Const.BACK_FILL_IN_CONTROLS, true)) + printRectangle = true; + + if (printRectangle && (border == 1 || backFill)) + GxDrawRect(left, top, right, bottom, border, foreColor, backFill ? 1 : 0, backColor, 0, 0, 0, 0, 0, 0, 0, 0); + + PdfCanvas canvas = new PdfCanvas(pdfPage); + sTxt = sTxt.TrimEnd(TRIM_CHARS); + + PdfFont font = baseFont; + canvas.SetFontAndSize(this.baseFont, fontSize); + canvas.SetFillColor(foreColor); + float captionHeight = baseFont.GetAscent(sTxt, fontSize) / 1000; + float rectangleWidth = baseFont.GetWidth(sTxt, fontSize); + float lineHeight = (1 / 1000) * (baseFont.GetAscent(sTxt, fontSize) - baseFont.GetDescent(sTxt, fontSize)) + (fontSize * 1.2f); + float textBlockHeight = (float)convertScale(bottom - top); + int linesCount = (int)(textBlockHeight / lineHeight); + int bottomOri = bottom; + int topOri = top; + + if (linesCount >= 2 && !((align & 16) == 16) && htmlformat != 1) + if (valign == (int)VerticalAlign.TOP) + bottom = top + (int)reconvertScale(lineHeight); + else if (valign == (int)VerticalAlign.BOTTOM) + top = bottom - (int)reconvertScale(lineHeight); + + float bottomAux = (float)convertScale(bottom) - ((float)convertScale(bottom - top) - captionHeight) / 2; + float topAux = (float)convertScale(top) + ((float)convertScale(bottom - top) - captionHeight) / 2; ; + + float leftAux = (float)convertScale(left); + float rightAux = (float)convertScale(right); + int alignment = align & 3; + bool autoResize = (align & 256) == 256; + + GXLogging.Debug(log, "GxDrawText left: " + left + ",top:" + top + ",right:" + right + ",bottom:" + bottom + ",captionHeight:" + captionHeight + ",fontSize:" + fontSize); + GXLogging.Debug(log, "GxDrawText leftAux: " + leftAux + ",leftMargin:" + leftMargin + ",pageSize.Top:" + pageSize.GetTop() + ",bottomAux:" + bottomAux + ",topMargin:" + topMargin + ",bottomMargin:" + bottomMargin); + if (htmlformat == 1) + { + try + { + ConverterProperties converterProperties = new ConverterProperties(); + FontProvider fontProvider = document.GetFontProvider(); + if (IsTrueType(baseFont)) + { + Hashtable locations = GetFontLocations(); + foreach (string fontName in locations.Keys) + { + string fontPath = (string)locations[fontName]; + if (string.IsNullOrEmpty(fontPath)) + { + MSPDFFontDescriptor fontDescriptor = new MSPDFFontDescriptor(); + fontPath = fontDescriptor.getTrueTypeFontLocation(fontName); + } + if (!string.IsNullOrEmpty(fontPath)) + { + + fontProvider.AddFont(fontPath); + } + } + } + converterProperties.SetFontProvider(fontProvider); + bottomAux = (float)convertScale(bottom); + topAux = (float)convertScale(top); + float drawingPageHeight = this.pageSize.GetTop() - topMargin - bottomMargin; + + float llx = leftAux + leftMargin; + float lly = drawingPageHeight - bottomAux; + float urx = rightAux + leftMargin; + float ury = drawingPageHeight - topAux; + + Rectangle htmlRectangle = new Rectangle(llx, lly, urx - llx, ury - lly); + YPosition yPosition = new YPosition(htmlRectangle.GetTop()); + + Canvas htmlCanvas = new Canvas(canvas, htmlRectangle); + htmlCanvas.SetFontProvider(fontProvider); + + //Iterate over the elements (a.k.a the parsed HTML string) and handle each case accordingly + IList elements = HtmlConverter.ConvertToElements(sTxt, converterProperties); + foreach (IElement element in elements) + { + float blockElementHeight = GetBlockElementHeight((IBlockElement)element, htmlRectangle); + + if (PageHeightExceeded(bottomMargin, yPosition.CurrentYPosition)) + { + llx = leftAux + leftMargin; + lly = drawingPageHeight - bottomAux; + urx = rightAux + leftMargin; + ury = drawingPageHeight - topAux; + htmlRectangle = new Rectangle(llx, lly, urx - llx, ury - lly); + yPosition = new YPosition(htmlRectangle.GetTop()); + bottomAux -= drawingPageHeight; + GxEndPage(); + GxStartPage(); + + canvas = new PdfCanvas(pdfPage); + sTxt = sTxt.TrimEnd(TRIM_CHARS); + canvas.SetFontAndSize(this.baseFont, fontSize); + canvas.SetFillColor(foreColor); + + htmlCanvas = new Canvas(canvas, htmlRectangle); + htmlCanvas.SetFontProvider(fontProvider); + } + ProcessHTMLElement((IBlockElement)element, alignment, htmlCanvas); + yPosition.CurrentYPosition -= blockElementHeight; + } + + } + catch (Exception ex1) + { + GXLogging.Debug(log, "Error adding html: ", ex1); + } + + } + else if (barcode != null) + { + GXLogging.Debug(log, "Barcode" + barcode.GetType().ToString()); + try + { + barcode.SetCode(sTxt); + barcode.SetTextAlignment(alignment); + Rectangle rectangle = new Rectangle(0, 0); + + switch (alignment) + { + case 1: // Center Alignment + rectangle = rectangle.SetBbox((leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + (float)this.pageSize.GetTop() - (float)convertScale(bottom) - topMargin - bottomMargin, + (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2, + (float)this.pageSize.GetTop() - (float)convertScale(top) - topMargin - bottomMargin); + break; + case 2: // Right Alignment + rectangle = rectangle.SetBbox(rightAux + leftMargin - rectangleWidth, + (float)this.pageSize.GetTop() - (float)convertScale(bottom) - topMargin - bottomMargin, + rightAux + leftMargin, + (float)this.pageSize.GetTop() - (float)convertScale(top) - topMargin - bottomMargin); + break; + case 0: // Left Alignment + rectangle = rectangle.SetBbox(leftAux + leftMargin, + (float)this.pageSize.GetTop() - (float)convertScale(bottom) - topMargin - bottomMargin, + leftAux + leftMargin + rectangleWidth, + (float)this.pageSize.GetTop() - (float)convertScale(top) - topMargin - bottomMargin); + break; + } + barcode.SetAltText(string.Empty); + barcode.SetBaseline(0); + + if (fontSize < Const.LARGE_FONT_SIZE) + barcode.SetX(Const.OPTIMAL_MINIMU_BAR_WIDTH_SMALL_FONT); + else + barcode.SetX(Const.OPTIMAL_MINIMU_BAR_WIDTH_LARGE_FONT); + + + Image imageCode = new Image(barcode.CreateFormXObject(backFill ? backColor : null, foreColor, pdfDocument)); + imageCode.SetFixedPosition(leftAux + leftMargin, rectangle.GetBottom()); + barcode.SetBarHeight(rectangle.GetHeight()); + imageCode.ScaleToFit(rectangle.GetWidth(), rectangle.GetHeight()); + document.Add(imageCode); + } + catch (Exception ex) + { + GXLogging.Error(log, "Error generating Barcode " + barcode.GetType().ToString(), ex); + } + } + else + { + + if (sTxt.Trim().ToLower().Equals("{{pages}}")) + { + GXLogging.Debug(log, "GxDrawText addTemplate-> (" + (leftAux + leftMargin) + "," + (this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin) + ") "); + templateRectangle = new Rectangle(leftAux + leftMargin, (float)this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin, rightAux + leftMargin, (float)this.pageSize.GetTop() - topAux - topMargin - bottomMargin); + + templatex = leftAux + leftMargin; + templatey = this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin; + templateFont = this.baseFont; + templateFontSize = fontSize; + templateColorFill = foreColor; + templateAlignment = alignment; + templateCreated = true; + } + + float textBlockWidth = rightAux - leftAux; + float TxtWidth = baseFont.GetWidth(sTxt, fontSize); + Boolean justified = (alignment == 3) && textBlockWidth < TxtWidth; + Boolean wrap = ((align & 16) == 16); + + float leading = (float)Convert.ToDouble(props.getGeneralProperty(Const.LEADING), CultureInfo.InvariantCulture.NumberFormat); + Style style = new Style(); + if (fontBold) style.SetBold(); + if (fontItalic) style.SetItalic(); + if (fontStrikethru) style.SetUnderline(fontSize / 6, fontSize / 2); + if (fontUnderline) style.SetUnderline(fontSize / 6, 0); + style.SetFont(font); + style.SetFontSize(fontSize); + style.SetFontColor(foreColor); + + if (wrap || justified) + { + bottomAux = (float)convertScale(bottomOri); + topAux = (float)convertScale(topOri); + + float llx = leftAux + leftMargin; + float lly = this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin; + float urx = rightAux + leftMargin; + float ury = this.pageSize.GetTop() - topAux - topMargin - bottomMargin; + + DrawTextColumn(llx, lly, urx, ury, leading, sTxt, valign, alignment, style, wrap); + } + else + { + try + { + if (!autoResize) + { + String newsTxt = sTxt; + while (TxtWidth > textBlockWidth && (newsTxt.Length - 1 >= 0)) + { + sTxt = newsTxt; + newsTxt = newsTxt.Substring(0, newsTxt.Length - 1); + TxtWidth = this.baseFont.GetWidth(newsTxt, fontSize); + } + } + + Paragraph p = new Paragraph(sTxt); + p.AddStyle(style); + + switch (alignment) + { + case 1: // Center Alignment + document.ShowTextAligned(p, ((leftAux + rightAux) / 2) + leftMargin, this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin, this.getPage(), TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0); + break; + case 2: // Right Alignment + document.ShowTextAligned(p, rightAux + leftMargin, this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin, this.getPage(), TextAlignment.RIGHT, VerticalAlignment.MIDDLE, 0); + break; + case 0: // Left Alignment + case 3: // Justified, only one text line + document.ShowTextAligned(p, leftAux + leftMargin, this.pageSize.GetTop() - bottomAux - topMargin - bottomMargin, this.getPage(), TextAlignment.LEFT, VerticalAlignment.MIDDLE, 0); + break; + } + } + catch (Exception e) + { + GXLogging.Error(log, "GxDrawText failed to draw simple text: ", e); + } + } + } + } + + private void ProcessHTMLElement(IBlockElement blockElement, int alignment, Canvas htmlCanvas) + { + if (blockElement is Paragraph p) + { + if (alignment != 0) + p.SetTextAlignment(GetTextAlignment(alignment)); + } + htmlCanvas.Add(blockElement); + } + bool PageHeightExceeded(float bottomAux, float drawingPageHeight) + { + return bottomAux > drawingPageHeight; + } + private float GetBlockElementHeight(IBlockElement blockElement, Rectangle htmlRectangle) + { + return blockElement.CreateRendererSubTree().SetParent(document.GetRenderer()).Layout(new LayoutContext(new LayoutArea(this.getPage(), htmlRectangle))).GetOccupiedArea().GetBBox().GetHeight(); + } + + //Utility class used to know where the cursor is left after each block element (HTML tag) is rendered + private class YPosition + { + public YPosition(float initialYPosition) + { + CurrentYPosition = initialYPosition; + } + + public float CurrentYPosition { get; set; } + } + + private BaseDirection? GetBaseDirection(int runDirection) + { + switch (runDirection) + { + case 2: return BaseDirection.LEFT_TO_RIGHT; + default: return null; + } + } + + private VerticalAlignment GetVerticalAlignment(float valign) + { + if (valign == (int)VerticalAlign.TOP) + return VerticalAlignment.TOP; + else if (valign == (int)VerticalAlign.BOTTOM) + return VerticalAlignment.BOTTOM; + else + return VerticalAlignment.MIDDLE; + } + + private TextAlignment? GetTextAlignment(int alignment) + { + switch (alignment) + { + case 1: return TextAlignment.CENTER; + case 2: return TextAlignment.RIGHT; + case 0: return TextAlignment.LEFT; + case 3: return TextAlignment.JUSTIFIED; + } + return null; + } + + void DrawTextColumn(float llx, float lly, float urx, float ury, float leading, String text, int valign, int alignment, Style style, Boolean wrap) + { + Paragraph p = new Paragraph(text); + + if (valign == (int)VerticalAlign.MIDDLE) + { + ury = ury + leading; + p.SetVerticalAlignment(VerticalAlignment.MIDDLE); + } + else if (valign == (int)VerticalAlign.BOTTOM) + { + ury = ury + leading; + p.SetVerticalAlignment(VerticalAlignment.BOTTOM); + } + else if (valign == (int)VerticalAlign.TOP) + { + ury = ury + leading / 2; + p.SetVerticalAlignment(VerticalAlignment.TOP); + } + Rectangle rect = new Rectangle(llx, lly, urx - llx, ury - lly); + p.SetTextAlignment(GetTextAlignment(alignment)); + p.AddStyle(style); + + if (wrap) + { + p.SetProperty(Property.SPLIT_CHARACTERS, new CustomSplitCharacters()); + Table table = new Table(1); + table.SetFixedPosition(this.getPage(), rect.GetX(), rect.GetY(), rect.GetWidth()); + Cell cell = new Cell(); + cell.SetWidth(rect.GetWidth()); + cell.SetHeight(rect.GetHeight()); + cell.SetBorder(Border.NO_BORDER); + cell.SetVerticalAlignment(VerticalAlignment.MIDDLE); + cell.Add(p); + table.AddCell(cell); + document.Add(table); + } + else + { + try + { + PdfCanvas pdfCanvas = new PdfCanvas(pdfPage); + Canvas canvas = new Canvas(pdfCanvas, rect); + canvas.Add(p); + canvas.Close(); + } + catch (Exception e) { GXLogging.Error(log, "GxDrawText failed to justify text column: ", e); } + } + } + + public class CustomSplitCharacters : DefaultSplitCharacters + { + public override bool IsSplitCharacter(GlyphLine text, int glyphPos) + { + if (!text.Get(glyphPos).HasValidUnicode()) + { + return false; + } + + bool baseResult = base.IsSplitCharacter(text, glyphPos); + bool myResult = false; + Glyph glyph = text.Get(glyphPos); + + if (glyph.GetUnicode() == '_') + { + myResult = true; + } + return myResult || baseResult; + } + } + +#pragma warning restore CS0612 // Type or member is obsolete + + private PageSize ComputePageSize(float leftMargin, float topMargin, int width, int length, bool marginsInsideBorder) + { + if ((leftMargin == 0 && topMargin == 0) || marginsInsideBorder) + { + if (length == 23818 && width == 16834) + return PageSize.A3; + else if (length == 16834 && width == 11909) + return PageSize.A4; + else if (length == 11909 && width == 8395) + return PageSize.A5; + else if (length == 20016 && width == 5731) + return PageSize.B4; + else if (length == 14170 && width == 9979) + return PageSize.B5; + else if (length == 15120 && width == 10440) + return PageSize.EXECUTIVE; + else if (length == 20160 && width == 12240) + return PageSize.LEGAL; + else if (length == 15840 && width == 12240) + return PageSize.LETTER; + else + return new PageSize(new Rectangle((int)(width / PAGE_SCALE_X), (int)(length / PAGE_SCALE_Y))); + } + return new PageSize(new Rectangle((int)(width / PAGE_SCALE_X) + leftMargin, (int)(length / PAGE_SCALE_Y) + topMargin)); + } + public override void GxEndDocument() + { + //{{Pages}} + if (templateCreated) + { + int totalPages = pdfDocument.GetNumberOfPages(); + for (int i = 0; i < totalPages; i++) + { + PdfPage page = pdfDocument.GetPage(i); + Canvas canvas = new Canvas(page, templateRectangle); + canvas.ShowTextAligned(i.ToString(CultureInfo.InvariantCulture), templatex, templatey, TextAlignment.CENTER).SetBackgroundColor(templateColorFill).SetFont(templateFont).SetFontSize(templateFontSize); + } + } + + int copies = 1; + try + { + copies = Convert.ToInt32(printerSettings.getProperty(form, Const.COPIES)); + GXLogging.Debug(log, "Setting number of copies to " + copies); + PdfViewerPreferences preferences = new PdfViewerPreferences(); + preferences.SetNumCopies(copies); + + int duplex = Convert.ToInt32(printerSettings.getProperty(form, Const.DUPLEX)); + PdfViewerPreferences.PdfViewerPreferencesConstants duplexValue; + if (duplex == 1) + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.SIMPLEX; + else if (duplex == 2) + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.DUPLEX_FLIP_LONG_EDGE; + else if (duplex == 3) + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.DUPLEX_FLIP_SHORT_EDGE; + else if (duplex == 4) + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.DUPLEX_FLIP_LONG_EDGE; + else + duplexValue = PdfViewerPreferences.PdfViewerPreferencesConstants.NONE; + GXLogging.Debug(log, "Setting duplex to " + duplexValue.ToString()); + preferences.SetDuplex(duplexValue); + } + catch (Exception ex) + { + GXLogging.Error(log, "Setting viewer preference error", ex); + } + + bool printingScript = false; + String serverPrinting = props.getGeneralProperty(Const.SERVER_PRINTING); + bool fit = props.getGeneralProperty(Const.ADJUST_TO_PAPER).Equals("true"); + if ((outputType == Const.OUTPUT_PRINTER || outputType == Const.OUTPUT_STREAM_PRINTER) && serverPrinting.Equals("false")) + { + + printingScript = true; + StringBuilder javascript = new StringBuilder(); ; + + javascript.Append("var pp = this.getPrintParams();"); + String printer = printerSettings.getProperty(form, Const.PRINTER).Replace("\\", "\\\\"); + if (!string.IsNullOrEmpty(printer)) + javascript.Append("pp.printerName = \"" + printer + "\";\n"); + + if (fit) + { + javascript.Append("pp.pageHandling = pp.constants.handling.fit;\n"); + } + else + { + javascript.Append("pp.pageHandling = pp.constants.handling.none;\n"); + } + + GXLogging.Debug(log, "MODE:" + printerSettings.getProperty(form, Const.MODE) + ",form:" + form); + + if (printerSettings.getProperty(form, Const.MODE, "3").StartsWith("0"))//Show printer dialog Never + { + javascript.Append("pp.interactive = pp.constants.interactionLevel.automatic;\n"); + + for (int i = 0; i < copies; i++) + { + javascript.Append("this.print(pp);\n"); + } + + //writer.addJavaScript("this.print({bUI: false, bSilent: true, bShrinkToFit: true});"); + //No print dialog is displayed. During printing a progress monitor and cancel + //dialog is displayed and removed automatically when printing is complete. + } + else //Show printer dialog is sent directly to printer | always + { + javascript.Append("pp.interactive = pp.constants.interactionLevel.full;\n"); + //Displays the print dialog allowing the user to change print settings and requiring + //the user to press OK to continue. During printing a progress monitor and cancel + //dialog is displayed and removed automatically when printing is complete. + + javascript.Append("this.print(pp);\n"); + + } + pdfDocument.GetCatalog().SetOpenAction(PdfAction.CreateJavaScript(javascript.ToString())); + } + + 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(); + GXLogging.Debug(log, "GxEndDocument!"); + try + { + props.save(); + GXLogging.Debug(log, "props.save()"); + } + catch (IOException e) + { + GXLogging.Error(log, "props.save() error", e); + + } + GXLogging.Debug(log, "outputType: " + outputType + ",docName:" + docName); + + switch (outputType) + { + case Const.OUTPUT_SCREEN: + try + { + outputStream.Close(); + GXLogging.Debug(log, "GxEndDocument OUTPUT_SCREEN outputstream length" + outputStream.ToString().Length); + } + catch (IOException e) + { + GXLogging.Error(log, "GxEndDocument OUTPUT_SCREEN error", e); + } + try { showReport(docName, modal); } + catch (Exception){} + break; + + case Const.OUTPUT_PRINTER: + try { outputStream.Close(); } + catch (IOException) {; } // Cierro el archivo + try + { + if (!serverPrinting.Equals("false") && !printingScript) + { + printReport(docName, this.printerOutputMode == 0, printerSettings.getProperty(form, Const.PRINTER)); + } + } + catch (Exception){} + break; + + case Const.OUTPUT_FILE: + try + { + outputStream.Close(); + GXLogging.Debug(log, "GxEndDocument OUTPUT_FILE outputstream length" + outputStream.ToString().Length); + } + catch (IOException e) + { + GXLogging.Error(log, "GxEndDocument OUTPUT_FILE error", e); + } + break; + + case Const.OUTPUT_STREAM: + case Const.OUTPUT_STREAM_PRINTER: + default: break; + } + outputStream = null; + + GXLogging.Debug(log, "GxEndDocument End"); + } + public override void GxStartPage() + { + try + { + pdfPage = pdfDocument.AddNewPage(); + GXLogging.Debug(log, "GxStartPage pages:" + pages + ",new page:" + pages + 1); + pages = pages + 1; + } + catch (PdfException de) + { + GXLogging.Error(log, "GxStartPage error", de); + } + } + + private bool IsTrueType(PdfFont font) + { + return font.GetFontProgram() is TrueTypeFont; + } + } +} + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs index 68e6152ae..5b9a30550 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs @@ -1,10 +1,15 @@ using System; -using Azure.Monitor.OpenTelemetry.AspNetCore; +using Azure.Identity; +using Azure.Monitor.OpenTelemetry.Exporter; using GeneXus.Services; using GeneXus.Services.OpenTelemetry; using log4net; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; namespace GeneXus.OpenTelemetry.Azure { @@ -17,23 +22,47 @@ public AzureAppInsights(GXService s) { } - public bool InstrumentAspNetCoreApplication(IServiceCollection services) + public bool InstrumentAspNetCoreApplication(IServiceCollection _) { string oltpEndpoint = Environment.GetEnvironmentVariable(APPLICATIONINSIGHTS_CONNECTION_STRING); - - if (!string.IsNullOrEmpty(oltpEndpoint)) + try { - services.AddOpenTelemetry() - .UseAzureMonitor( o => - { + var resourceBuilder = ResourceBuilder.CreateDefault() + .AddTelemetrySdk(); + + Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(resourceBuilder) + .AddAzureMonitorTraceExporter(o => + { + if (!string.IsNullOrEmpty(oltpEndpoint)) o.ConnectionString = oltpEndpoint; - }); + else + { + o.Credential = new DefaultAzureCredential(); + log.Debug("Connect to Azure monitor Opentelemetry Trace exporter using Default Azure credential"); + } + }) + .AddGxAspNetInstrumentation() + .Build(); + Sdk.CreateMeterProviderBuilder() + .SetResourceBuilder(resourceBuilder) + .AddAzureMonitorMetricExporter(o => + { + if (!string.IsNullOrEmpty(oltpEndpoint)) + o.ConnectionString = oltpEndpoint; + else + { + o.Credential = new DefaultAzureCredential(); + log.Debug("Connect to Azure monitor Opentelemetry Metrics exporter using Default Azure credential"); + } + }) + .Build(); return true; } - else - { - log.Warn("OpenTelemetry Azure Monitor was not initialized due to missing 'APPLICATIONINSIGHTS_CONNECTION_STRING' Environment Variable"); + catch (Exception ex) + { + log.Warn("Azure Monitor Opentelemetry could not be initialized. " + ex.Message); return false; } } diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj index f8d09b91f..26bbe19b1 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj @@ -10,7 +10,15 @@ NU1605 - + + + + + + + + + diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs index 64ff15c7f..bd44d6bda 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs @@ -5961,9 +5961,12 @@ public static string RoundBorders(string imageFile, int topLeftRadius, int topRi g.FillPath(Brushes.White, path); - using (TextureBrush br = new TextureBrush(new Bitmap(OriginalImage), WrapMode.Clamp)) + using (Bitmap bitmap = new Bitmap(OriginalImage)) { - g.FillPath(br, path); + using (TextureBrush br = new TextureBrush(bitmap, WrapMode.Clamp)) + { + g.FillPath(br, path); + } } } } diff --git a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs index 7254ff5e8..80b273938 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs @@ -778,6 +778,7 @@ public class Preferences static int oldSTR = -1; static int instrumented = -1; static string mediaPath; + static string pdfLib; static string blobPath; static string blobPathFolderName; static int blankEmptyDates = -1; @@ -1192,6 +1193,28 @@ public static string getPRINT_LAYOUT_METADATA_DIR() } return mediaPath; } + internal static string PdfReportLibrary() + { + if (pdfLib == null) + { + lock (syncRoot) + { + if (pdfLib == null) + { + if (Config.GetValueOf("PDF_RPT_LIBRARY", out pdfLib)) + { + pdfLib = pdfLib.Trim(); + } + else + { + pdfLib = string.Empty; + } + GXLogging.Debug(log, "PDF_RPT_LIBRARY:", pdfLib); + } + } + } + return pdfLib; + } public enum StorageTimeZonePty { Undefined = 0, Utc = 1, Local = 2 }; public static Boolean useTimezoneFix() diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs index 14ed3a8be..1b4470a85 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs @@ -1,26 +1,25 @@ +using System; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Reflection; +using System.Security; +using System.Text; using GeneXus.Application; using GeneXus.Cache; using GeneXus.Utils; +using GxClasses.Helpers; using log4net; using MySQLCommand = MySqlConnector.MySqlCommand; -using MySQLParameter = MySqlConnector.MySqlParameter; using MySQLConnection = MySqlConnector.MySqlConnection; -using MySQLException = MySqlConnector.MySqlException; -using MySQLDbType = MySqlConnector.MySqlDbType; using MySQLDataAdapter = MySqlConnector.MySqlDataAdapter; -using System.IO; -using GxClasses.Helpers; -using System.Reflection; -using System; -using System.Data; -using System.Data.Common; -using System.Text; -using System.Collections.Generic; -using System.Security; +using MySQLDbType = MySqlConnector.MySqlDbType; +using MySQLException = MySqlConnector.MySqlException; +using MySQLParameter = MySqlConnector.MySqlParameter; namespace GeneXus.Data { - + public class GxMySqlConnector : GxDataRecord { static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Data.GxMySqlConnector)); @@ -49,27 +48,9 @@ public override GxAbstractConnectionWrapper GetConnection(bool showPrompt, strin return new MySqlConnectorConnectionWrapper(m_connectionString, connectionCache, isolationLevel); } - string convertToMySqlCall(string stmt, GxParameterCollection parameters) - { - if (parameters == null) - return ""; - string pname; - StringBuilder sBld = new StringBuilder(); - for (int i = 0; i < parameters.Count; i++) - { - if (i > 0) - sBld.Append(", "); - pname = "@" + parameters[i].ParameterName; - sBld.Append(pname); - parameters[i].ParameterName = pname; - } - return "CALL " + stmt + "(" + sBld.ToString() + ")"; - } [SecuritySafeCritical] public override IDbCommand GetCommand(IGxConnection con, string stmt, GxParameterCollection parameters, bool isCursor, bool forFirst, bool isRpc) { - if (isRpc) - stmt = convertToMySqlCall(stmt, parameters); MySQLCommand mysqlcmd = (MySQLCommand)base.GetCommand(con, stmt, parameters.Distinct()); if (isCursor && !isRpc) { diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs index 6da308291..e1484d23d 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs @@ -60,7 +60,10 @@ public int Add(IDataParameter value) { return parameters.Add(value); } - + internal void Reverse() + { + parameters.Reverse(); + } public void Clear() { parameters.Clear(); @@ -158,6 +161,7 @@ public GxParameterCollection Distinct() parms.Add(this[j].ParameterName); } } + uniqueParms.Reverse(); return uniqueParms; } else diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs index debd2d04b..0ba575f00 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs @@ -41,6 +41,7 @@ public class MultiPartTemplate { public string Boundary; public string FormdataTemplate; + public string FormdataSeparator; public byte[] Boundarybytes; public byte[] EndBoundaryBytes; public string HeaderTemplate; @@ -50,7 +51,8 @@ internal MultiPartTemplate() { Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x"); ContentType = $"multipart/form-data; boundary={Boundary}"; - FormdataTemplate = "\r\n--" + Boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}"; + FormdataSeparator = "\r\n"; + FormdataTemplate = "--" + Boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}"; Boundarybytes = Encoding.ASCII.GetBytes($"\r\n--{Boundary}\r\n"); EndBoundaryBytes = Encoding.ASCII.GetBytes($"\r\n--{Boundary}--"); HeaderTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" + "Content-Type: {2}\r\n\r\n"; @@ -431,7 +433,7 @@ void SendVariables(Stream reqStream) for (int i = 0; i < _formVars.Count; i++) { if (_formVars.Keys[i] != null) - vars.Add(buildVariableToSend(_formVars.Keys[i], _formVars[i])); + vars.Add(buildVariableToSend(_formVars.Keys[i], _formVars[i], vars.Count)); } if (vars.Count > 0) { @@ -457,11 +459,15 @@ string variableSeparator() else return "&"; } - string buildVariableToSend(string key, string value) + string buildVariableToSend(string key, string value, int idx) { + bool needsCLRF = idx > 0; if (IsMultipart) { - return string.Format(MultiPart.FormdataTemplate, key, value); + if (needsCLRF) + return MultiPart.FormdataSeparator + string.Format(MultiPart.FormdataTemplate, key, value); + else + return string.Format(MultiPart.FormdataTemplate, key, value); } else { diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxLocations.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxLocations.cs index e8b2bc1eb..8ef8be5b2 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxLocations.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxLocations.cs @@ -16,6 +16,7 @@ public class GxLocation string _resourceName = ""; short _authentication; short _authenticationMethod; + string _accessToken = ""; string _authenticationUser = ""; string _authenticationRealm = ""; string _authenticationPassword = ""; @@ -76,6 +77,11 @@ public short AuthenticationMethod get {return _authenticationMethod;} set {_authenticationMethod = value;} } + public string AccessToken + { + get { return _accessToken; } + set { _accessToken = value; } + } public string AuthenticationUser { get {return _authenticationUser;} diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs new file mode 100644 index 000000000..f2b346659 --- /dev/null +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs @@ -0,0 +1,53 @@ +using System.Net.Http; +using System.Security; +using System.Web; +using System.Web.Helpers; +using GeneXus.Application; +using GeneXus.Utils; + +namespace GeneXus.Http +{ + internal class CSRFHelper + { + [SecuritySafeCritical] + internal static void ValidateAntiforgery(HttpContext context) + { + if (RestAPIHelpers.ValidateCsrfToken()) + { + ValidateAntiforgeryImpl(context); + } + } + [SecurityCritical] + static void ValidateAntiforgeryImpl(HttpContext context) + { + string cookieToken, formToken; + string httpMethod = context.Request.HttpMethod; + string tokens = context.Request.Cookies[HttpHeader.X_CSRF_TOKEN_COOKIE]?.Value; + string internalCookieToken = context.Request.Cookies[HttpHeader.X_CSRF_TOKEN_COOKIE]?.Value; + if (httpMethod == HttpMethod.Get.Method && (string.IsNullOrEmpty(tokens) || string.IsNullOrEmpty(internalCookieToken))) + { + AntiForgery.GetTokens(null, out cookieToken, out formToken); +#pragma warning disable SCS0009 // The cookie is missing security flag HttpOnly + HttpCookie cookie = new HttpCookie(HttpHeader.X_CSRF_TOKEN_COOKIE, formToken) + { + HttpOnly = false, + Secure = GxContext.GetHttpSecure(context) == 1, + }; +#pragma warning restore SCS0009 // The cookie is missing security flag HttpOnly + HttpCookie internalCookie = new HttpCookie(AntiForgeryConfig.CookieName, cookieToken) + { + HttpOnly = true, + Secure = GxContext.GetHttpSecure(context) == 1, + }; + context.Response.SetCookie(cookie); + context.Response.SetCookie(internalCookie); + } + if (httpMethod == HttpMethod.Delete.Method || httpMethod == HttpMethod.Post.Method || httpMethod == HttpMethod.Put.Method) + { + cookieToken = context.Request.Cookies[AntiForgeryConfig.CookieName]?.Value; + string headerToken = context.Request.Headers[HttpHeader.X_CSRF_TOKEN_HEADER]; + AntiForgery.Validate(cookieToken, headerToken); + } + } + } +} diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs index 6cc2ff359..bc372fc0b 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs @@ -371,6 +371,10 @@ public void RestExecute() httpClient.AddHeader("Content-Type", _contentType); } } + if (this.Location.AuthenticationMethod == 4 && !String.IsNullOrEmpty(this.Location.AccessToken)) + { + httpClient.AddHeader("Authorization", this.Location.AccessToken); + } string serviceuri = ((this.Location.Secure > 0) ? "https" : "http") + "://" + this.Location.Host; serviceuri += (this.Location.Port != 80) ? ":" + this.Location.Port.ToString() : String.Empty; serviceuri += "/" + this.Location.BaseUrl.TrimEnd('/').TrimStart('/') + "/" + this.Location.ResourceName; diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index 4fd91c649..213448533 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -50,7 +50,8 @@ public class HttpHeader public static string XGXFILENAME = "x-gx-filename"; internal static string ACCEPT = "Accept"; internal static string TRANSFER_ENCODING = "Transfer-Encoding"; - internal static string X_GXCSRF_TOKEN = "X-GXCSRF-TOKEN"; + internal static string X_CSRF_TOKEN_HEADER = "X-XSRF-TOKEN"; + internal static string X_CSRF_TOKEN_COOKIE = "XSRF-TOKEN"; } internal class HttpHeaderValue { diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs index 98ffd93e3..0398b09dd 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs @@ -86,6 +86,11 @@ private void onPostResolveRequestCache(object sender, EventArgs eventArgs) if (apiHandler != null) HttpContext.Current.RemapHandler(apiHandler); } + else if (string.Equals(HttpContext.Current.Request.HttpMethod, HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase) && + HttpContext.Current.Request.Path.EndsWith("/" + REST_BASE_URL, StringComparison.OrdinalIgnoreCase)) + { + CSRFHelper.ValidateAntiforgery(HttpContext.Current); + } } void IHttpModule.Dispose() { diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs index b945d0df5..596278e73 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs @@ -19,12 +19,15 @@ namespace GeneXus.HttpHandlerFactory internal class OptionsApiObjectRequestHandler : IHttpHandler { string actualPath; + string regexpPath; string objectName; - internal OptionsApiObjectRequestHandler(string path, string name) + internal OptionsApiObjectRequestHandler(string path, string name, string regexp) { actualPath = path; objectName = name; + regexpPath = regexp; } + public void ProcessRequest(HttpContext context) { // OPTIONS VERB @@ -32,7 +35,7 @@ public void ProcessRequest(HttpContext context) bool found = false; foreach (Tuple t in GXAPIModule.servicesMapData[actualPath].Keys) { - if (t.Item1.Equals(objectName.ToLower())) + if (t.Item1.Equals(objectName.ToLower()) || (GxRegex.IsMatch( t.Item1,regexpPath))) { mthheaders.Add(t.Item2); found = true; @@ -61,14 +64,14 @@ class HandlerFactory : IHttpHandlerFactory public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) { - IHttpHandler handlerToReturn; + IHttpHandler handlerToReturn; string relativeURL = context.Request.AppRelativeCurrentExecutionFilePath; string fname = relativeURL.Substring(relativeURL.LastIndexOf('~') + 2); String cname1 = (fname.Contains(".")) ? fname.Substring(0, fname.LastIndexOf('.')) : fname; string cname0 = cname1.ToLower(); string actualPath = ""; - + if (cname0 == "gxoauthlogout") { return new GeneXus.Http.GXOAuthLogout(); @@ -85,7 +88,7 @@ public IHttpHandler GetHandler(HttpContext context, string requestType, string u { return new GeneXus.Http.GXMultiCall(); } - string assemblyName, cname; + string assemblyName, cname; if (GXAPIModule.serviceInPath(pathTranslated, actualPath: out actualPath)) { string nspace; @@ -96,33 +99,37 @@ public IHttpHandler GetHandler(HttpContext context, string requestType, string u String objectNameUp = GetObjFromPath(cname1, actualPath); // Dictionary routeParms; - if (GXAPIModule.servicesMapData.ContainsKey(actualPath) && GetSMap(actualPath, objectName, objectNameUp, requestType, out string mapName, out routeParms)) + if (GXAPIModule.servicesMapData.ContainsKey(actualPath)) { - if (!String.IsNullOrEmpty(mapName) && GXAPIModule.servicesMap[actualPath].TryGetValue(mapName, out SingleMap value)) + bool IsServiceCall = GetSMap(actualPath, objectName, objectNameUp, requestType, out string mapName, out string mapRegExp, out routeParms); + if (IsServiceCall) { - String tmpController = objClass; - String asssemblycontroller = tmpController; - if (objClass.Contains("\\")) + if (!String.IsNullOrEmpty(mapName) && GXAPIModule.servicesMap[actualPath].TryGetValue(mapName, out SingleMap value)) { - tmpController = objClass.Substring(objClass.LastIndexOf("\\") + 1); - String addNspace = objClass.Substring(0, objClass.LastIndexOf("\\")).Replace("\\", "."); - asssemblycontroller = addNspace + "." + tmpController; - nspace += "." + addNspace; + String tmpController = objClass; + String asssemblycontroller = tmpController; + if (objClass.Contains("\\")) + { + tmpController = objClass.Substring(objClass.LastIndexOf("\\") + 1); + String addNspace = objClass.Substring(0, objClass.LastIndexOf("\\")).Replace("\\", "."); + asssemblycontroller = addNspace + "." + tmpController; + nspace += "." + addNspace; + } + GxContext gxContext = GxContext.CreateDefaultInstance(); + object handler = ClassLoader.FindInstance(asssemblycontroller, nspace, tmpController, new Object[] { gxContext }, null); + + gxContext.HttpContext = context; + GxRestWrapper restWrapper = new Application.GxRestWrapper(handler as GXBaseObject, context, gxContext, value.ServiceMethod, value.VariableAlias, routeParms); + return restWrapper; } - GxContext gxContext = GxContext.CreateDefaultInstance(); - object handler = ClassLoader.FindInstance(asssemblycontroller, nspace, tmpController, new Object[] { gxContext }, null); - - gxContext.HttpContext = context; - GxRestWrapper restWrapper = new Application.GxRestWrapper(handler as GXBaseObject, context, gxContext, value.ServiceMethod, value.VariableAlias, routeParms); - return restWrapper; } - } - else - { - if ( requestType.Equals(HttpMethod.Options.Method) && !String.IsNullOrEmpty(actualPath) && GXAPIModule.servicesMapData.ContainsKey(actualPath)) + else { - return new OptionsApiObjectRequestHandler(actualPath, objectName); - } + if (requestType.Equals(HttpMethod.Options.Method) && !String.IsNullOrEmpty(actualPath) && GXAPIModule.servicesMapData.ContainsKey(actualPath)) + { + return new OptionsApiObjectRequestHandler(actualPath, objectName, mapRegExp); + } + } } return null; } @@ -197,30 +204,36 @@ public string GetObjFromPath(string cname, string apath) return objectName; } - public bool GetSMap(string actualPath, string objectName, string objectNameUp, string requestType, out string mapName, out Dictionary routeParms) + public bool GetSMap(string actualPath, string objectName, string objectNameUp, string requestType, out string mapName, out string mapRegexp, out Dictionary routeParms) { routeParms = null; if (GXAPIModule.servicesMapData[actualPath].TryGetValue(Tuple.Create(objectName, requestType), out mapName)) - { + { + mapRegexp = mapName; return true; } else { + mapRegexp = mapName; foreach (SingleMap m in GXAPIModule.servicesMap[actualPath].Values) { if (!m.Path.Equals(m.PathRegexp) && GxRegex.IsMatch(objectName, m.PathRegexp)) { - mapName = m.Name; - routeParms = new Dictionary(); - int i=0; - foreach (string smatch in ((GxRegexMatch)GxRegex.Matches(objectNameUp, m.PathRegexp, RegexOptions.Multiline | RegexOptions.IgnoreCase)[0]).Groups) - { - string var = ((GxRegexMatch)GxRegex.Matches(m.Path, m.PathRegexp)[0]).Groups[i]; - var = var.Substring(1, var.Length -2); - routeParms.Add(var, smatch); - i++; + mapName = m.Name; + mapRegexp = m.PathRegexp; + if (m.Verb.Equals(requestType)) + { + routeParms = new Dictionary(); + int i = 0; + foreach (string smatch in ((GxRegexMatch)GxRegex.Matches(objectNameUp, m.PathRegexp, RegexOptions.Multiline | RegexOptions.IgnoreCase)[0]).Groups) + { + string var = ((GxRegexMatch)GxRegex.Matches(m.Path, m.PathRegexp)[0]).Groups[i]; + var = var.Substring(1, var.Length - 2); + routeParms.Add(var, smatch); + i++; + } + return true; } - return true; } } } diff --git a/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs b/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs index 7ec76f78e..967201629 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs @@ -349,12 +349,94 @@ protected void initialize(int objClass, int objId, int dbgLines, long hash) protected void trkrng(int lineNro, int lineNro2) => dbgInfo?.TrkRng(lineNro, 0, lineNro2, 0); protected void trkrng(int lineNro, int colNro, int lineNro2, int colNro2) => dbgInfo?.TrkRng(lineNro, colNro, lineNro2, colNro2); } + public class GXDataGridProcedure : GXProcedure + { + static readonly ILog log = log4net.LogManager.GetLogger(typeof(GXDataGridProcedure)); + + const string HAS_NEXT_PAGE = "HasNextPage"; + const string RECORD_COUNT = "RecordCount"; + const string RECORD_COUNT_SUPPORTED = "RecordCountSupported"; + long totalRecordCount = -1; + protected virtual long RecordCount() + { + return -1; + } + protected virtual bool RecordCountSupported() + { + return true; + } + protected void SetPaginationHeaders(bool hasNextPage) + { + try + { + SetHasNextPageHeader(hasNextPage); + SetRecordCountSupportedHeader(); + } + catch (Exception ex) + { + GXLogging.Warn(log, $"A processing error occurred while setting pagination headers", ex); + } + } + private void SetRecordCountSupportedHeader() + { + if (!RecordCountSupported()) + { + GXLogging.Debug(log, $"Adding '{RECORD_COUNT_SUPPORTED}' header"); + context.SetHeader(RECORD_COUNT_SUPPORTED, false.ToString()); + } + } + private void SetHasNextPageHeader(bool hasNextPage) + { + context.SetHeader(HAS_NEXT_PAGE, StringUtil.BoolToStr(hasNextPage)); + } + + private void SetRecordCountHeader() + { + bool recordCountHeaderRequired = false; + bool setHeader = false; + if (context.HttpContext != null) + { + recordCountHeaderRequired = !string.IsNullOrEmpty(context.HttpContext.Request.Headers[RECORD_COUNT]); + } + if (totalRecordCount != -1) + { + setHeader = true; + } + else if (recordCountHeaderRequired) + { + totalRecordCount = RecordCount(); + setHeader = true; + } + if (setHeader) + { + GXLogging.Debug(log, $"Adding '{RECORD_COUNT}' header:", totalRecordCount.ToString()); + context.SetHeader(RECORD_COUNT, totalRecordCount.ToString()); + } + } + protected long GetPaginationStart(long start, long count) + { + if (start < 0) //last page + { + totalRecordCount = RecordCount(); + long lastPageRecords = totalRecordCount % count; + if (lastPageRecords == 0) + start = totalRecordCount - count; + else + start = totalRecordCount - lastPageRecords; + } + SetRecordCountHeader(); + return start; + } + } public class GxReportUtils { public static int OUTPUT_RVIEWER_NATIVE = 1; public static int OUTPUT_RVIEWER_DLL = 2; public static int OUTPUT_PDF = 3; +#if NETCORE + const string PDF_LIBRARY_ITEXT8 = "ITEXT8"; +#endif static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); static public IReportHandler GetPrinter( int outputType, string path, Stream reportOutputStream) @@ -389,7 +471,8 @@ static public IReportHandler GetPrinter( int outputType, string path, Stream rep Type classType = assem.GetType( "GeneXus.Printer.GxReportBuilderPdf", false, true); reportHandler = (IReportHandler) Activator.CreateInstance(classType,new Object[]{path, reportOutputStream}); #else - reportHandler = (IReportHandler)(ClassLoader.FindInstance("GxPdfReportsCS", "GeneXus.Printer", "GxReportBuilderPdf", new Object[] { path, reportOutputStream }, null)); + string reportBuidler = Preferences.PdfReportLibrary().Equals(PDF_LIBRARY_ITEXT8, StringComparison.OrdinalIgnoreCase) ? "GxReportBuilderPdf8" : "GxReportBuilderPdf"; + reportHandler = (IReportHandler)(ClassLoader.FindInstance("GxPdfReportsCS", "GeneXus.Printer", reportBuidler, new Object[] { path, reportOutputStream }, null)); #endif } catch (TargetInvocationException ex) diff --git a/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs b/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs index e13ebb200..9eb75a4d3 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs @@ -558,11 +558,8 @@ void AddHeader(string header, string value) [SecuritySafeCritical] public bool ProcessHeaders(string queryId) { - if (RestAPIHelpers.ValidateCsrfToken()) - { - ValidateAntiforgery(); - } - + CSRFHelper.ValidateAntiforgery(context.HttpContext); + NameValueCollection headers = GetHeaders(); String language = null, theme = null, etag = null; if (headers != null) @@ -601,38 +598,7 @@ public bool ProcessHeaders(string queryId) return true; } - [SecurityCritical] - private void ValidateAntiforgery() - { - string cookieToken, formToken; - string httpMethod = context.HttpContext.Request.HttpMethod; - string tokens = context.HttpContext.Request.Cookies[HttpHeader.X_GXCSRF_TOKEN]?.Value; - string internalCookieToken = context.HttpContext.Request.Cookies[HttpHeader.X_GXCSRF_TOKEN]?.Value; - if (httpMethod == HttpMethod.Get.Method && (string.IsNullOrEmpty(tokens) || string.IsNullOrEmpty(internalCookieToken))) - { - AntiForgery.GetTokens(null, out cookieToken, out formToken); -#pragma warning disable SCS0009 // The cookie is missing security flag HttpOnly - HttpCookie cookie = new HttpCookie(HttpHeader.X_GXCSRF_TOKEN, formToken) - { - HttpOnly = false, - Secure = context.GetHttpSecure() == 1, - }; -#pragma warning restore SCS0009 // The cookie is missing security flag HttpOnly - HttpCookie internalCookie = new HttpCookie(AntiForgeryConfig.CookieName, cookieToken) - { - HttpOnly = true, - Secure = context.GetHttpSecure() == 1, - }; - context.HttpContext.Response.SetCookie(cookie); - context.HttpContext.Response.SetCookie(internalCookie); - } - if (httpMethod == HttpMethod.Delete.Method || httpMethod == HttpMethod.Post.Method || httpMethod == HttpMethod.Put.Method) - { - cookieToken = context.HttpContext.Request.Cookies[AntiForgeryConfig.CookieName]?.Value; - string headerToken = context.HttpContext.Request.Headers[HttpHeader.X_GXCSRF_TOKEN]; - AntiForgery.Validate(cookieToken, headerToken); - } - } + private void SendCacheHeaders() { diff --git a/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs b/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs index c0b1c6a74..7de521c68 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs @@ -136,14 +136,14 @@ public virtual Task MethodBodyExecute(object key) { innerMethod = this._serviceMethod; bodyParameters = PreProcessApiSdtParameter( _procWorker, innerMethod, bodyParameters, this._variableAlias); - } + } + ServiceHeaders(); Dictionary outputParameters = ReflectionHelper.CallMethod(_procWorker, innerMethod, bodyParameters, _gxContext); Dictionary formatParameters = ReflectionHelper.ParametersFormat(_procWorker, innerMethod); setWorkerStatus(_procWorker); _procWorker.cleanup(); RestProcess(_procWorker, outputParameters); wrapped = GetWrappedStatus(_procWorker, wrapped, outputParameters, outputParameters.Count); - ServiceHeaders(); return Serialize(outputParameters, formatParameters, wrapped); } catch (Exception e) @@ -312,6 +312,7 @@ public virtual Task MethodUrlExecute(object key) string innerMethod = EXECUTE_METHOD; Dictionary outputParameters; Dictionary formatParameters = new Dictionary(); + ServiceHeaders(); if (!string.IsNullOrEmpty(_serviceMethodPattern)) { innerMethod = _serviceMethodPattern; @@ -332,7 +333,6 @@ public virtual Task MethodUrlExecute(object key) RestProcess(_procWorker, outputParameters); bool wrapped = false; wrapped = GetWrappedStatus(_procWorker, wrapped, outputParameters, parCount); - ServiceHeaders(); return Serialize(outputParameters, formatParameters, wrapped); } catch (Exception e) @@ -709,7 +709,7 @@ string dateTimeToHTMLDate(DateTime dt) } public Task WebException(Exception ex) { -#if NETCORE +#if NETCORE GxHttpActivitySourceHelper.SetException(Activity.Current, ex); #endif GXLogging.Error(log, "WebException", ex); diff --git a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext.cs b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs similarity index 51% rename from dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext.cs rename to dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs index 61b8d0b30..a656d7d07 100644 --- a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext.cs +++ b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs @@ -1,3 +1,4 @@ + using System; using System.IO; @@ -11,18 +12,14 @@ using System.util; using System.Diagnostics; using log4net; -using iTextSharp.text.pdf; -using iTextSharp.text; using GeneXus.Printer; -using iTextSharp.text.html.simpleparser; using System.Collections.Generic; using System.Security; using GeneXus; using GeneXus.Utils; using System.Reflection; using GeneXus.Metadata; -using GeneXus.Configuration; namespace com.genexus.reports { @@ -34,75 +31,63 @@ internal enum VerticalAlign BOTTOM = 2, } - public class PDFReportItextSharp : IReportHandler + public abstract class PDFReportItextBase : IReportHandler { - private int lineHeight, pageLines; + protected int lineHeight, pageLines; static ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - private iTextSharp.text.Rectangle pageSize; - private BaseFont baseFont; - Barcode barcode = null; - private bool fontUnderline; - private bool fontStrikethru; - private int fontSize; - private string language; - - //Color for, BaseColor for => Itext5 - private object backColor, foreColor, templateColorFill; - private Stream outputStream = null; - - private static object syncRoot = new Object(); - private static ParseINI props; - private ParseINI printerSettings; - private String form; - private ArrayList stringTotalPages; - private int outputType = -1; - private int printerOutputMode = -1; - private bool modal = false; - private String docName = "PDFReport.pdf"; - private static NativeSharpFunctionsMS nativeCode = new NativeSharpFunctionsMS(); - private static Hashtable fontSubstitutes = new Hashtable(); - private static String configurationFile = null; - private static String configurationTemplateFile = null; - private static String defaultRelativePrepend = null; - private static String webAppDir = null; + protected bool fontUnderline; + protected bool fontStrikethru; + protected int fontSize; + protected string language; + + protected Stream outputStream = null; + + protected static object syncRoot = new Object(); + protected static ParseINI props; + protected ParseINI printerSettings; + protected String form; + protected ArrayList stringTotalPages; + protected int outputType = -1; + protected int printerOutputMode = -1; + protected bool modal = false; + protected String docName = "PDFReport.pdf"; + protected static NativeSharpFunctionsMS nativeCode = new NativeSharpFunctionsMS(); + protected Hashtable fontSubstitutes = new Hashtable(); + protected static String configurationFile = null; + protected static String configurationTemplateFile = null; + protected static String defaultRelativePrepend = null; + protected static String webAppDir = null; public static bool DEBUG = false; - private Document document; - private PdfWriter writer; - private static String predefinedSearchPath = ""; - private float leftMargin; - private float topMargin; - private float bottomMargin; - private PdfTemplate template; - private BaseFont templateFont; - private int templateFontSize; - private bool backFill = true; - private int templateAlignment; - private int pages = 0; - private bool templateCreated = false; - private bool asianFontsDllLoaded = false; - private static char[] TRIM_CHARS = { ' ' }; - private int M_top, M_bot; + protected static String predefinedSearchPath = ""; + protected float leftMargin; + protected float topMargin; + protected float bottomMargin; + protected int templateFontSize; + protected bool backFill = true; + protected int templateAlignment; + protected int pages = 0; + protected bool templateCreated = false; + protected static char[] TRIM_CHARS = { ' ' }; + protected int M_top, M_bot; public static float DASHES_UNITS_ON = 10; public static float DASHES_UNITS_OFF = 10; public static float DOTS_UNITS_OFF = 3; public static float DOTS_UNITS_ON = 1; public bool lineCapProjectingSquare = true; public bool barcode128AsImage = true; - private PdfConformanceLevel complianceLevel = PdfConformanceLevel.None; - internal Dictionary documentImages; - float[] STYLE_SOLID = new float[] { 1, 0 };//0 - float[] STYLE_NONE = null;//1 - float[] STYLE_DOTTED, //2 + protected PdfConformanceLevel complianceLevel = PdfConformanceLevel.None; + protected float[] STYLE_SOLID = new float[] { 1, 0 };//0 + protected float[] STYLE_NONE = null;//1 + protected float[] STYLE_DOTTED, //2 STYLE_DASHED, //3 STYLE_LONG_DASHED, //4 STYLE_LONG_DOT_DASHED; //5 - int STYLE_NONE_CONST = 1; - int runDirection; - int justifiedType; - static Assembly iTextAssembly = typeof(Document).Assembly; + protected int STYLE_NONE_CONST = 1; + protected int runDirection; + protected int MULTIPLIED_LEADING = 1; public bool GxOpenDoc(string fileName) { @@ -247,14 +232,11 @@ public static void showReport(String filename1, bool modal) } private static char alternateSeparator = Path.DirectorySeparatorChar == '/' ? '\\' : '/'; - public PDFReportItextSharp(String appPath) + public PDFReportItextBase(String appPath) { try { - document = null; - pageSize = null; stringTotalPages = new ArrayList(); - documentImages = new Dictionary(); defaultRelativePrepend = appPath; webAppDir = defaultRelativePrepend; if (appPath.Length > 0) @@ -417,27 +399,31 @@ public static String getPredefinedSearchPaths() return predefinedSearchPath; } - private void init() + protected virtual void init(ref int gxYPage, ref int gxXPage, int pageWidth, int pageLength) { - Document.Compress = true; - try + + } + private float[] parsePattern(String patternStr) + { + if (patternStr!=null) { - writer = PdfWriter.GetInstance(document, outputStream); - string level = props.getGeneralProperty(Const.COMPLIANCE_LEVEL); - if (Enum.TryParse(level, true, out complianceLevel)) + patternStr = patternStr.Trim(); + String[] values = patternStr.Split(new char[]{';'}); + if (values.Length>0) { - if (SetComplainceLevel(complianceLevel)) - writer.SetTagged(); + float[] pattern = new float[values.Length]; + for (int i=0; i