diff --git a/API/Controllers/DocumentProcessingController.cs b/API/Controllers/DocumentProcessingController.cs new file mode 100644 index 0000000..1beb1cf --- /dev/null +++ b/API/Controllers/DocumentProcessingController.cs @@ -0,0 +1,595 @@ +using API; +using DocumentProcessing; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Diagnostics; +using System.Globalization; +using System.Net.Http.Headers; +using Telerik.Documents.Common.Model; +using Telerik.Documents.Core.Fonts; +using Telerik.Documents.Media; +using Telerik.Documents.Primitives; +using Telerik.Windows.Documents.Fixed.Model; +using Telerik.Windows.Documents.Fixed.Model.ColorSpaces; +using Telerik.Windows.Documents.Fixed.Model.Editing; +using Telerik.Windows.Documents.Fixed.Model.Editing.Flow; +using Telerik.Windows.Documents.Fixed.Model.Graphics; +using Telerik.Windows.Documents.Flow.Model; +using Telerik.Windows.Documents.Flow.Model.Editing; +using Telerik.Windows.Documents.Flow.Model.Styles; +using Telerik.Windows.Documents.Spreadsheet.Model; +using Telerik.Windows.Documents.Spreadsheet.Utilities; +using Telerik.Zip; + +[ApiController] +[Route("documentprocessing")] +[Authorize] +public class DocumentProcessingController : ControllerBase { + + private readonly IPdfProcessing _pdfProcessing; + private readonly IWordProcessing _wordProcessing; + private readonly ISpreadProcessing _spreadProcessing; + private readonly IZipProcessing _zipProcessing; + private static readonly List _uploadedFiles =new List(); + public DocumentProcessingController(IPdfProcessing pdfProcessing, IWordProcessing wordProcessing, ISpreadProcessing spreadProcessing, IZipProcessing zipProcessing) { + _pdfProcessing = pdfProcessing; + _wordProcessing = wordProcessing; + _spreadProcessing = spreadProcessing; + _zipProcessing = zipProcessing; + } + [HttpGet] + [Route("exporttopdf")] + [AccessCodeAuthorize("AA01")] + public async Task ExportToPDF() { + RadFixedDocument? document = DocumentGenerator.CreateDocument(); + WaterMark waterMark = new WaterMark { + Text = "Water Mark Demo", + Transparency = 100, + Angle = -45 + }; + _pdfProcessing.AddWaterMark(document, waterMark); + var pdfFile = _pdfProcessing.GetPDFByte(document); + return File(pdfFile, "application/pdf"); + } + [HttpGet] + [Route("mergepdf")] + [AccessCodeAuthorize("AA01")] + public async Task MergePDF() { + RadFixedDocument? document = DocumentGenerator.CreateDocument(); + List documentList = new List { + document, + document + }; + var pdfFile = _pdfProcessing.MergePDFToByte(documentList); + return File(pdfFile, "application/pdf"); + } + [HttpGet] + [Route("exporttodocx")] + [AccessCodeAuthorize("AA01")] + public async Task ExportToDocx() { + RadFlowDocument? document = DocumentGenerator.CreateFlowDocument(); + var wordFile =_wordProcessing.GetWordByte(DocumentFormat.docx, document); + if (wordFile == null) { + return NotFound(); + } + return File(wordFile, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + } + [HttpGet] + [Route("exporttoxlsx")] + [AccessCodeAuthorize("AA01")] + public async Task ExportToXlsx() { + Workbook workbook = DocumentGenerator.CreateWorkbook(); + var excelFile =_spreadProcessing.GetXlsxByte(workbook); + if (excelFile == null) { + return NotFound(); + } + return File(excelFile, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + } + [HttpPost] + [Route("uploadfiles")] + [AccessCodeAuthorize("AA01")] + public async Task UploadFiles(IEnumerable files) { + if (files != null) { + IFormFile? file =files.FirstOrDefault(); + if (file == null) { + return new NotFoundResult(); + } + FileDetails fileDetails = new FileDetails { + Name = ContentDispositionHeaderValue.Parse(file.ContentDisposition)?.FileName?.ToString()?.Trim('"') ?? "" + }; + fileDetails.Data = fileDetails.ReadToEnd(file.OpenReadStream()); + _uploadedFiles.Add(fileDetails); + } + return new OkResult(); + } + [HttpGet] + [Route("zipfiles")] + [AccessCodeAuthorize("AA01")] + public async Task ZipFiles(string password) { + DeflateSettings compressionSettings = new DeflateSettings { + CompressionLevel = CompressionLevel.Best, + HeaderType = CompressedStreamHeader.ZLib + }; + DefaultEncryptionSettings encryptionSettings = new DefaultEncryptionSettings { + Password = password + }; + Dictionary zipArchiveFiles=new Dictionary(); + foreach (FileDetails? fileItem in _uploadedFiles) { + zipArchiveFiles[fileItem.Name] = new MemoryStream(fileItem.Data); + } + var zipFile = _zipProcessing.GetZipBytes(zipArchiveFiles, entryNameEncoding: null, compressionSettings, encryptionSettings); + return File(zipFile, "application/x-zip-compressed"); + } + +} + + +public static class DocumentGenerator { + private static readonly double defaultLeftIndent = 50; + private static readonly double defaultLineHeight = 16; + private static readonly int IndexColumnQuantity = 5; + private static readonly int IndexColumnUnitPrice = 6; + private static readonly int IndexColumnSubTotal = 7; + private static readonly int IndexRowItemStart = 1; + + private static readonly string AccountFormatString = GenerateCultureDependentFormatString(); + private static readonly ThemableColor InvoiceBackground = new ThemableColor(Color.FromArgb(255, 44, 62, 80)); + private static readonly ThemableColor InvoiceHeaderForeground = new ThemableColor(Color.FromArgb(255, 255, 255, 255)); + + + public static RadFixedDocument CreateDocument() { + RadFixedDocument document = new RadFixedDocument(); + RadFixedPage page = document.Pages.AddPage(); + page.Size = new Size(600, 800); + FixedContentEditor editor = new FixedContentEditor(page); + editor.Position.Translate(defaultLeftIndent, 50); + double currentTopOffset = 110; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + double maxWidth = page.Size.Width - defaultLeftIndent * 2; + DrawDescription(editor, maxWidth); + currentTopOffset += defaultLineHeight * 4; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + using (editor.SaveProperties()) { + DrawFunnelFigure(editor); + } + editor.Position.Translate(defaultLeftIndent * 4, page.Size.Height - 100); + DrawText(editor, maxWidth); + return document; + } + private static void DrawDescription(FixedContentEditor editor, double maxWidth) { + Block block = new Block(); + block.GraphicProperties.FillColor = RgbColors.Black; + block.HorizontalAlignment = HorizontalAlignment.Left; + block.TextProperties.FontSize = 14; + block.TextProperties.TrySetFont(new FontFamily("Calibri"), FontStyles.Italic, FontWeights.Bold); + block.InsertText("Document Processing"); + block.TextProperties.TrySetFont(new FontFamily("Calibri")); + block.InsertText(" is a document processing library that enables your application to import and export files to and from PDF format. The document model is entirely independent from UI and allows you to generate sleek documents with differently formatted text, images, shapes and more."); + + editor.DrawBlock(block, new Size(maxWidth, double.PositiveInfinity)); + } + private static void DrawFunnelFigure(FixedContentEditor editor) { + editor.GraphicProperties.IsStroked = false; + editor.GraphicProperties.FillColor = new RgbColor(231, 238, 247); + editor.DrawEllipse(new Point(250, 70), 136, 48); + + editor.GraphicProperties.IsStroked = true; + editor.GraphicProperties.StrokeColor = RgbColors.White; + editor.GraphicProperties.StrokeThickness = 1; + editor.GraphicProperties.FillColor = new RgbColor(91, 155, 223); + editor.DrawEllipse(new Point(289, 77), 48, 48); + + editor.Position.Translate(291, 204); + CenterText(editor, "Fonts"); + + editor.Position.Translate(0, 0); + editor.DrawEllipse(new Point(238, 274), 48, 48); + editor.Position.Translate(190, 226); + CenterText(editor, "Images"); + + editor.Position.Translate(0, 0); + editor.DrawEllipse(new Point(307, 347), 48, 48); + editor.Position.Translate(259, 299); + CenterText(editor, "Shapes"); + + editor.Position.Translate(0, 0); + PathGeometry arrow = new PathGeometry(); + PathFigure figure = arrow.Figures.AddPathFigure(); + figure.StartPoint = new Point(287, 422); + figure.IsClosed = true; + figure.Segments.AddLineSegment(new Point(287, 438)); + figure.Segments.AddLineSegment(new Point(278, 438)); + figure.Segments.AddLineSegment(new Point(300, 454)); + figure.Segments.AddLineSegment(new Point(322, 438)); + figure.Segments.AddLineSegment(new Point(313, 438)); + figure.Segments.AddLineSegment(new Point(313, 422)); + + editor.DrawPath(arrow); + + editor.GraphicProperties.FillColor = new RgbColor(80, 255, 255, 255); + editor.GraphicProperties.IsStroked = true; + editor.GraphicProperties.StrokeThickness = 1; + editor.GraphicProperties.StrokeColor = new RgbColor(91, 155, 223); + + PathGeometry funnel = new PathGeometry { + FillRule = FillRule.EvenOdd + }; + figure = funnel.Figures.AddPathFigure(); + figure.IsClosed = true; + figure.StartPoint = new Point(164, 245); + figure.Segments.AddArcSegment(new Point(436, 245), 136, 48); + figure.Segments.AddArcSegment(new Point(164, 245), 136, 48); + + figure = funnel.Figures.AddPathFigure(); + figure.IsClosed = true; + figure.StartPoint = new Point(151, 245); + figure.Segments.AddArcSegment(new Point(449, 245), 149, 61); + figure.Segments.AddLineSegment(new Point(332, 415)); + figure.Segments.AddArcSegment(new Point(268, 415), 16, 4); + + editor.DrawPath(funnel); + + editor.Position.Translate(164, 455); + Block block = new Block(); + block.TextProperties.Font = editor.TextProperties.Font; + block.GraphicProperties.FillColor = RgbColors.Black; + block.HorizontalAlignment = HorizontalAlignment.Center; + block.VerticalAlignment = Telerik.Windows.Documents.Fixed.Model.Editing.Flow.VerticalAlignment.Top; + block.TextProperties.FontSize = 18; + block.InsertText("Document"); + editor.DrawBlock(block, new Size(272, double.PositiveInfinity)); + } + private static void CenterText(FixedContentEditor editor, string text) { + Block block = new Block(); + block.TextProperties.TrySetFont(new FontFamily("Calibri")); + block.HorizontalAlignment = HorizontalAlignment.Center; + block.VerticalAlignment = Telerik.Windows.Documents.Fixed.Model.Editing.Flow.VerticalAlignment.Center; + block.GraphicProperties.FillColor = RgbColors.White; + block.InsertText(text); + + editor.DrawBlock(block, new Size(96, 96)); + } + private static void DrawText(FixedContentEditor editor, double maxWidth) { + double currentTopOffset = 470; + currentTopOffset += defaultLineHeight * 2; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + editor.TextProperties.FontSize = 11; + + Block block = new Block(); + block.TextProperties.FontSize = 11; + block.TextProperties.TrySetFont(new FontFamily("Arial")); + block.InsertText("A wizard's job is to vex "); + using (block.GraphicProperties.Save()) { + block.GraphicProperties.FillColor = new RgbColor(255, 146, 208, 80); + block.InsertText("chumps"); + } + + block.InsertText(" quickly in fog."); + editor.DrawBlock(block, new Size(maxWidth, double.PositiveInfinity)); + + currentTopOffset += defaultLineHeight; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + + block = new Block(); + block.TextProperties.FontSize = 11; + block.TextProperties.TrySetFont(new FontFamily("Trebuchet MS")); + block.InsertText("A wizard's job is to vex chumps "); + using (block.TextProperties.Save()) { + block.TextProperties.UnderlinePattern = Telerik.Windows.Documents.Fixed.Model.Editing.Flow.UnderlinePattern.Single; + block.TextProperties.UnderlineColor = editor.GraphicProperties.FillColor; + block.InsertText("quickly"); + } + + block.InsertText(" in fog."); + editor.DrawBlock(block, new Size(maxWidth, double.PositiveInfinity)); + + currentTopOffset += defaultLineHeight; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + block = new Block(); + block.TextProperties.FontSize = 11; + block.TextProperties.TrySetFont(new FontFamily("Algerian")); + block.InsertText("A "); + using (block.TextProperties.Save()) { + block.TextProperties.UnderlinePattern = Telerik.Windows.Documents.Fixed.Model.Editing.Flow.UnderlinePattern.Single; + block.TextProperties.UnderlineColor = editor.GraphicProperties.FillColor; + block.InsertText("wizard's"); + } + + block.InsertText(" job is to vex chumps quickly in fog."); + editor.DrawBlock(block, new Size(maxWidth, double.PositiveInfinity)); + + currentTopOffset += defaultLineHeight; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + + editor.TextProperties.TrySetFont(new FontFamily("Lucida Calligraphy")); + editor.DrawText("A wizard's job is to vex chumps quickly in fog.", new Size(maxWidth, double.PositiveInfinity)); + + currentTopOffset += defaultLineHeight + 2; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + block = new Block(); + block.TextProperties.FontSize = 11; + block.TextProperties.TrySetFont(new FontFamily("Consolas")); + block.InsertText("A wizard's job is to vex chumps "); + using (block.TextProperties.Save()) { + block.TextProperties.TrySetFont(new FontFamily("Consolas"), FontStyles.Normal, FontWeights.Bold); + block.InsertText("quickly"); + } + + block.InsertText(" in fog."); + editor.DrawBlock(block, new Size(maxWidth, double.PositiveInfinity)); + + currentTopOffset += defaultLineHeight; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + editor.TextProperties.TrySetFont(new FontFamily("Arial")); + + editor.DrawText("Document testing", new Size(maxWidth, double.PositiveInfinity)); + + currentTopOffset += defaultLineHeight; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + editor.DrawText("Document testing", new Size(maxWidth, double.PositiveInfinity)); + + currentTopOffset += defaultLineHeight; + editor.Position.Translate(defaultLeftIndent, currentTopOffset); + editor.DrawText("Document testing", new Size(maxWidth, double.PositiveInfinity)); + + } + public static RadFlowDocument CreateFlowDocument() { + RadFlowDocument document = new RadFlowDocument(); + RadFlowDocumentEditor editor = new RadFlowDocumentEditor(document); + editor.ParagraphFormatting.TextAlignment.LocalValue = Alignment.Justified; + + // Body + editor.InsertLine("Dear Telerik User,"); + editor.InsertText("We’re happy to introduce the Telerik RadWordsProcessing component. High performance library that enables you to read, write and manipulate documents in DOCX, RTF and plain text format. The document model is independent from UI and "); + Run run = editor.InsertText("does not require"); + run.Underline.Pattern = Telerik.Windows.Documents.Flow.Model.Styles.UnderlinePattern.Single; + editor.InsertLine(" Microsoft Office."); + + editor.InsertText("The current community preview version comes with full rich-text capabilities including "); + editor.InsertText("bold, ").FontWeight = FontWeights.Bold; + editor.InsertText("italic, ").FontStyle = FontStyles.Italic; + editor.InsertText("underline,").Underline.Pattern = Telerik.Windows.Documents.Flow.Model.Styles.UnderlinePattern.Single; + editor.InsertText(" font sizes and ").FontSize = Telerik.Windows.Documents.Media.Unit.PointToDip(20); + Run coloredRun = editor.InsertText("colors "); + coloredRun.ForegroundColor = new ThemableColor(Color.FromArgb(255, 92, 230, 0)); + coloredRun.Shading.BackgroundColor = new ThemableColor(Color.FromArgb(255, 255, 255, 0)); + + editor.InsertLine("as well as text alignment and indentation. Other options include tables, hyperlinks, inline and floating images. Even more sweetness is added by the built-in styles and themes."); + + editor.InsertText("Here at Telerik we strive to provide the best services possible and fulfill all needs you as a customer may have. We would appreciate any feedback you send our way through the "); + editor.InsertHyperlink("public forums", "http://www.telerik.com/forums", false, "Telerik Forums"); + editor.InsertLine(" or support ticketing system."); + + editor.InsertLine("We hope you’ll enjoy RadWordsProcessing as much as we do. Happy coding!"); + editor.InsertParagraph(); + editor.InsertText("Kind regards,"); + + CreateHeader(editor); + + CreateFooter(editor); + + return document; + } + private static void CreateFooter(RadFlowDocumentEditor editor) { + Footer footer = editor.Document.Sections.First().Footers.Add(); + Paragraph paragraph = footer.Blocks.AddParagraph(); + paragraph.TextAlignment = Alignment.Right; + paragraph.Inlines.AddRun("www.telerik.com"); + + editor.MoveToParagraphStart(paragraph); + } + + private static void CreateHeader(RadFlowDocumentEditor editor) { + Header header = editor.Document.Sections.First().Headers.Add(); + Paragraph paragraph = header.Blocks.AddParagraph(); + paragraph.TextAlignment = Alignment.Right; + paragraph.Inlines.AddRun("Demo"); + + editor.MoveToParagraphStart(header.Blocks.AddParagraph()); + } + public static Workbook CreateWorkbook() { + Workbook workbook = new Workbook(); + workbook.Sheets.Add(SheetType.Worksheet); + + Worksheet worksheet = workbook.ActiveWorksheet; + List? products =new Products().GetData(20).ToList(); + PrepareInvoiceDocument(worksheet, products.Count); + + int currentRow = IndexRowItemStart + 1; + foreach (Product product in products) { + worksheet.Cells[currentRow, 0].SetValue(product.Name); + worksheet.Cells[currentRow, IndexColumnQuantity].SetValue(product.Quantity); + worksheet.Cells[currentRow, IndexColumnUnitPrice].SetValue(product.UnitPrice); + worksheet.Cells[currentRow, IndexColumnSubTotal].SetValue(product.SubTotal); + + currentRow++; + } + + return workbook; + } + private static void PrepareInvoiceDocument(Worksheet worksheet, int itemsCount) { + int lastItemIndexRow = IndexRowItemStart + itemsCount; + + CellIndex firstUsedCellIndex = new CellIndex(0, 0); + CellIndex lastUsedCellIndex = new CellIndex(lastItemIndexRow + 1, IndexColumnSubTotal); + CellBorder border = new CellBorder(CellBorderStyle.DashDot, InvoiceBackground); + worksheet.Cells[firstUsedCellIndex, lastUsedCellIndex].SetBorders(new CellBorders(border, border, border, border, null, null, null, null)); + + worksheet.Cells[firstUsedCellIndex].SetValue("INVOICE"); + worksheet.Cells[firstUsedCellIndex].SetFontSize(20); + worksheet.Cells[firstUsedCellIndex].SetHorizontalAlignment(RadHorizontalAlignment.Center); + worksheet.Cells[0, 0, 0, IndexColumnSubTotal].MergeAcross(); + + worksheet.Columns[IndexColumnUnitPrice].SetWidth(new ColumnWidth(125, true)); + worksheet.Columns[IndexColumnSubTotal].SetWidth(new ColumnWidth(125, true)); + + worksheet.Cells[IndexRowItemStart, 0].SetValue("Item"); + worksheet.Cells[IndexRowItemStart, IndexColumnQuantity].SetValue("QTY"); + worksheet.Cells[IndexRowItemStart, IndexColumnUnitPrice].SetValue("Unit Price"); + worksheet.Cells[IndexRowItemStart, IndexColumnSubTotal].SetValue("SubTotal"); + worksheet.Cells[IndexRowItemStart, 0, lastItemIndexRow, IndexColumnQuantity - 1].MergeAcross(); + + worksheet.Cells[IndexRowItemStart, 0, IndexRowItemStart, IndexColumnSubTotal].SetFill + (new GradientFill(GradientType.Horizontal, InvoiceBackground, InvoiceBackground)); + worksheet.Cells[IndexRowItemStart, 0, IndexRowItemStart, IndexColumnSubTotal].SetForeColor(InvoiceHeaderForeground); + worksheet.Cells[IndexRowItemStart, IndexColumnUnitPrice, lastItemIndexRow, IndexColumnSubTotal].SetFormat( + new CellValueFormat(AccountFormatString)); + + worksheet.Cells[lastItemIndexRow + 1, 6].SetValue("TOTAL: "); + worksheet.Cells[lastItemIndexRow + 1, 7].SetFormat(new CellValueFormat(AccountFormatString)); + + string subTotalColumnCellRange = NameConverter.ConvertCellRangeToName( + new CellIndex(IndexRowItemStart + 1, IndexColumnSubTotal), + new CellIndex(lastItemIndexRow, IndexColumnSubTotal)); + + worksheet.Cells[lastItemIndexRow + 1, IndexColumnSubTotal].SetValue(string.Format("=SUM({0})", subTotalColumnCellRange)); + + worksheet.Cells[lastItemIndexRow + 1, IndexColumnUnitPrice, lastItemIndexRow + 1, IndexColumnSubTotal].SetFontSize(20); + } + private static string GenerateCultureDependentFormatString() { + string gS = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator; + string dS = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + + return "_($* #" + gS + "##0" + dS + "00_);_($* (#" + gS + "##0" + dS + "00);_($* \"-\"??_);_(@_)"; + } +} +public class Product { + private int id; + private string name; + private double unitPrice; + private int quantity; + private DateTime date; + private double subTotal; + + public Product(int id, string name, double unitPrice, int quantity, DateTime date) { + ID = id; + Name = name; + UnitPrice = unitPrice; + Quantity = quantity; + Date = date; + SubTotal = this.quantity * this.unitPrice; + } + + public int ID { + get => id; + set { + if (id != value) { + id = value; + } + } + } + + public string Name { + get => name; + set { + if (name != value) { + name = value; + } + } + } + + public double UnitPrice { + get => unitPrice; + set { + if (unitPrice != value) { + unitPrice = value; + } + } + } + + public int Quantity { + get => quantity; + set { + if (quantity != value) { + quantity = value; + } + } + } + + public DateTime Date { + get => date; + set { + if (date != value) { + date = value; + } + } + } + + public double SubTotal { + get => subTotal; + set { + if (subTotal != value) { + subTotal = value; + } + } + } +} +public class Products { + private static readonly string[] names = new string[] { "Côte de Blaye", "Boston Crab Meat", + "Singaporean Hokkien Fried Mee", "Gula Malacca", "Rogede sild", + "Spegesild", "Zaanse koeken", "Chocolade", "Maxilaku", "Valkoinen suklaa" }; + private static readonly double[] prizes = new double[] { 23.2500, 9.0000, 45.6000, 32.0000, + 14.0000, 19.0000, 263.5000, 18.4000, 3.0000, 14.0000 }; + private static readonly DateTime[] dates = new DateTime[] { new DateTime(2007, 5, 10), new DateTime(2008, 9, 13), + new DateTime(2008, 2, 22), new DateTime(2009, 1, 2), new DateTime(2007, 4, 13), + new DateTime(2008, 5, 12), new DateTime(2008, 1, 19), new DateTime(2008, 8, 26), + new DateTime(2008, 7, 31), new DateTime(2007, 7, 16) }; + + + public IEnumerable GetData(int itemCount) { + Random rnd = new Random(); + + return from i in Enumerable.Range(1, itemCount) + select new Product(i, + names[rnd.Next(9)], + prizes[rnd.Next(9)], + rnd.Next(1, 9), + dates[rnd.Next(9)]); + } +} +public class FileDetails { + public string Name { get; set; } + public byte[] Data { get; set; } + public byte[] ReadToEnd(System.IO.Stream stream) { + long originalPosition = 0; + + if (stream.CanSeek) { + originalPosition = stream.Position; + stream.Position = 0; + } + + try { + byte[] readBuffer = new byte[4096]; + + int totalBytesRead = 0; + int bytesRead; + + while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0) { + totalBytesRead += bytesRead; + + if (totalBytesRead == readBuffer.Length) { + int nextByte = stream.ReadByte(); + if (nextByte != -1) { + byte[] temp = new byte[readBuffer.Length * 2]; + Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length); + Buffer.SetByte(temp, totalBytesRead, (byte)nextByte); + readBuffer = temp; + totalBytesRead++; + } + } + } + + byte[] buffer = readBuffer; + if (readBuffer.Length != totalBytesRead) { + buffer = new byte[totalBytesRead]; + Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead); + } + return buffer; + } finally { + if (stream.CanSeek) { + stream.Position = originalPosition; + } + } + } + +} \ No newline at end of file diff --git a/API/Controllers/LoginController.cs b/API/Controllers/LoginController.cs index c9e3908..cb8b845 100644 --- a/API/Controllers/LoginController.cs +++ b/API/Controllers/LoginController.cs @@ -28,28 +28,36 @@ public LoginController(IConfiguration config, public async Task Get() { JwtSecurityToken jwtToken; string token; + AuthResult authResult; - if (HttpContext.User.Identity.Name == "" || HttpContext.User.Identity.Name == null) { + if (HttpContext.User.Identity!.Name == "" || HttpContext.User.Identity.Name == null) { throw new InvalidUserException(); } if (jwtUtil.ValidateToken(HttpContext.Request, out jwtToken, out token)) { - return Ok(new AuthResult() { + if (HttpContext.User.Identity.Name == jwtToken.Claims + .Where(c => c.Type == ClaimTypes.Name) + .Select(c => c.Value).SingleOrDefault()) { + Array.ForEach(jwtToken.Claims.Where(c => c.Type == ClaimTypes.Role) + .ToArray(), c => ((ClaimsIdentity)HttpContext.User.Identity).AddClaim(c)); + } + authResult = new AuthResult() { Token = token, Success = true, RefreshToken = "" - }); + }; + } else { + List? claims = _service.GetUserClaims(HttpContext.User.Identity.Name); + + ClaimsIdentity claimsIdentity = (ClaimsIdentity)HttpContext.User.Identity; + Array.ForEach(claims.Where(c => c.Type == ClaimTypes.Role).ToArray(), + c => claimsIdentity.AddClaim(c)); + authResult = jwtUtil.GenerateJwtToken(HttpContext.User.Identity.Name, claims); } - List? claims = _service.GetUserClaims(HttpContext.User.Identity.Name); - - ClaimsIdentity claimsIdentity = (ClaimsIdentity)HttpContext.User.Identity; - Array.ForEach(claims.Where(c => c.Type == ClaimTypes.Role).ToArray(), - c => claimsIdentity.AddClaim(c)); - AntiforgeryTokenSet? tokens = antiforgery.GetAndStoreTokens(HttpContext); - HttpContext.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false }); + HttpContext.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken!, new CookieOptions() { HttpOnly = false }); - return Ok(jwtUtil.GenerateJwtToken(HttpContext.User.Identity.Name, claims)); + return Ok(authResult); } } diff --git a/API/Controllers/SystemParameters/SystemParametersController.cs b/API/Controllers/SystemParameters/SystemParametersController.cs index 98b96af..e14b31b 100644 --- a/API/Controllers/SystemParameters/SystemParametersController.cs +++ b/API/Controllers/SystemParameters/SystemParametersController.cs @@ -7,6 +7,7 @@ namespace API { [ApiController] [Route("systemparameters")] + [AutoValidateAntiforgeryToken] public class SystemParametersController : ControllerBase { private readonly SystemParametersService _service; private readonly ILogger _logger; @@ -27,7 +28,6 @@ public async Task SearchAll([FromBody]SystemParametersSearchReque [HttpPost] [Route("addsystemparameter")] [AccessCodeAuthorize("SP02")] - [ValidateAntiForgeryToken] public async Task AddSystemParameter([FromBody]AddSystemParameterRequest request) { AddDataResponse response; request.Refresh(HttpContext.User.Identity.Name,DateTime.Now); @@ -37,7 +37,6 @@ public async Task AddSystemParameter([FromBody]AddSystemParameter [HttpPost] [Route("editsystemparameter")] [AccessCodeAuthorize("SP03")] - [ValidateAntiForgeryToken] public async Task EditSystemParameter([FromBody] EditSystemParameterRequest request) { EditDataResponse response; request.Refresh(HttpContext.User.Identity.Name, DateTime.Now); @@ -47,7 +46,6 @@ public async Task EditSystemParameter([FromBody] EditSystemParame [HttpGet] [Route("deletesystemparameter")] [AccessCodeAuthorize("SP04")] - [ValidateAntiForgeryToken] public async Task DeleteSystemParameter([FromQuery] DeleteSystemParameterRequest request) { EditDataResponse response; response = await _service.DeleteSystemParameterAsync(request); diff --git a/API/Controllers/UserController.cs b/API/Controllers/UserController.cs index 99739e1..5a8b13c 100644 --- a/API/Controllers/UserController.cs +++ b/API/Controllers/UserController.cs @@ -8,6 +8,7 @@ namespace API { [ApiController] [Route("users")] + [AutoValidateAntiforgeryToken] public class UserController : ControllerBase { private readonly UserService _service; private readonly ILogger _logger; @@ -40,7 +41,6 @@ public async Task Get([FromBody] GetUserRequest request) { [HttpPost(Name = "AddNewUser")] [AccessCodeAuthorize("AB01")] - [ValidateAntiForgeryToken] public async Task Add([FromBody] AddUserRequest request) { AddUserResponse response; request.Refresh(HttpContext.User.Identity.Name, DateTime.Now); @@ -50,7 +50,6 @@ public async Task Add([FromBody] AddUserRequest request) { [HttpPost("Addpayslip")] [AccessCodeAuthorize("AC01")] - [ValidateAntiForgeryToken] public async Task AddPayslip([FromBody] AddPayslipRequest request) { AddPayslipResponse _response; request.Refresh(HttpContext.User.Identity.Name, DateTime.Now); diff --git a/API/P1.API.csproj b/API/P1.API.csproj index 7158203..69216f4 100644 --- a/API/P1.API.csproj +++ b/API/P1.API.csproj @@ -39,12 +39,17 @@ + + + + + diff --git a/API/Program.cs b/API/Program.cs index 1e0a292..3e7dc58 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -91,10 +91,10 @@ } else { builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate(); - builder.Services.AddMvc(options => { - options.Filters.Add(); - }); } +builder.Services.AddMvc(options => { + options.Filters.Add(); +}); builder.Services.AddAuthorization(options => { options.FallbackPolicy = options.DefaultPolicy; }); diff --git a/API/RegisterModule.cs b/API/RegisterModule.cs index 7cb48aa..642fae9 100644 --- a/API/RegisterModule.cs +++ b/API/RegisterModule.cs @@ -4,6 +4,7 @@ using Service; using MediatR; using Microsoft.AspNetCore.Authorization; +using DocumentProcessing; namespace API { public class RegisterModule : Module { @@ -50,6 +51,10 @@ protected override void Load(ContainerBuilder builder) { builder.RegisterType().As() .SingleInstance(); //builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); } } diff --git a/Client/Client.csproj b/Client/Client.csproj index 8fc5ff9..ba5ef01 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -21,6 +21,7 @@ + diff --git a/Client/Pages/DocumentProcessing.razor b/Client/Pages/DocumentProcessing.razor new file mode 100644 index 0000000..ed4e772 --- /dev/null +++ b/Client/Pages/DocumentProcessing.razor @@ -0,0 +1,89 @@ +@page "/documentprocessing" +@using System.Diagnostics +@inject HttpUtil _httpUtil +@inject IJSRuntime JsRuntime + +

Document Processing

+ + + + + +
+ +
+ + +
Accepted files: DOCX, PDF, JPG and PNG
+
+ + +
+ +
+
+
+ +
+ +@code { + public bool IsLoad { get; set; } = false; + public string Password { get; set; } + public async Task PasswordChanged(string value) { + Password = value; + } + public async Task ExportPDF() { + IsLoad = true; + var response = await _httpUtil.GetAsync("documentprocessing/exporttopdf"); + var fileData = await response.Content.ReadAsByteArrayAsync(); + Save(fileData, "application/pdf", "Sample Document.pdf"); + } + + public async Task MergePDF() { + IsLoad = true; + var response = await _httpUtil.GetAsync("documentprocessing/mergepdf"); + var fileData = await response.Content.ReadAsByteArrayAsync(); + Save(fileData, "application/pdf", "Merge Sample Document.pdf"); + } + public async Task ExportDocx() { + IsLoad = true; + var response = await _httpUtil.GetAsync("documentprocessing/exporttodocx"); + var fileData = await response.Content.ReadAsByteArrayAsync(); + Save(fileData, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "Sample Document.docx"); + } + public async Task ExportXlsx() { + IsLoad = true; + var response = await _httpUtil.GetAsync("documentprocessing/exporttoxlsx"); + var fileData = await response.Content.ReadAsByteArrayAsync(); + Save(fileData, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Sample Document.xlsx"); + + } + public string SaveUrl => "documentprocessing/uploadfiles"; + public List AllowedExtensions { get; set; } = new List() { ".docx", ".pdf", ".jpg", ".png" }; + public int MinFileSize { get; set; } = 1024; + public int MaxFileSize { get; set; } = 4 * 1024 * 1024; + public async Task UploadHandler(UploadEventArgs e) { + e.RequestHeaders = new Dictionary(); + await _httpUtil.RefreshToken(e.RequestHeaders); + } + public async Task ZipFiles() { + IsLoad = true; + var response = await _httpUtil.GetAsync($"documentprocessing/zipfiles?password={Password}"); + var fileData = await response.Content.ReadAsByteArrayAsync(); + Save(fileData, "application/x-zip-compressed", "ZipDemo.zip"); + } + public void Save(byte[] byteData, string mimeType, string fileName) { + if (byteData == null) { + return; + } + var fileBase64=Convert.ToBase64String(byteData); + JsRuntime.InvokeVoidAsync("saveFile", fileBase64, mimeType, fileName); + IsLoad = false; + } +} diff --git a/Client/Shared/NavMenu/DrawerMenu.cs b/Client/Shared/NavMenu/DrawerMenu.cs index cc2ccaa..2d3149a 100644 --- a/Client/Shared/NavMenu/DrawerMenu.cs +++ b/Client/Shared/NavMenu/DrawerMenu.cs @@ -18,6 +18,7 @@ public static class DrawerMenu { new DrawerItem{ Text = "Search User", Icon = "dollar", Url="/dotnet6EAA/searchuser", Group = "app"}, new DrawerItem{ Text = "Report", Icon = "dollar", Url="/dotnet6EAA/Report", Group = "app"}, new DrawerItem{ Text = "System Parameters", Icon = "tell-a-friend", Url="/dotnet6EAA/systemparameters/searchdatas", Group = "app"}, + new DrawerItem{ Text = "Document Processing", Icon = "tell-a-friend", Url="/dotnet6EAA/documentprocessing", Group = "app"}, new DrawerItem{ Text = "Swagger UI", Icon="gear", Url="/dotnet6EAA/swagger/index.html",Group="settings",Target= MenuTarget.Blank.Code}, }; } diff --git a/Client/Util/GeneralSearchUtil.cs b/Client/Util/GeneralSearchUtil.cs index 765edf3..4f7349d 100644 --- a/Client/Util/GeneralSearchUtil.cs +++ b/Client/Util/GeneralSearchUtil.cs @@ -65,6 +65,7 @@ private async Task PostData(HttpContent jsonContent, string apiUrl) { Notspinning = true; if (response == null) { Message = "Response data is null"; + return; } var content = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { @@ -85,6 +86,7 @@ public async Task DeleteAsync(string apiUrl) { Notspinning = true; if (response == null) { Message = "Response data is null"; + return; } var content = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { diff --git a/Client/Util/HttpUtil.cs b/Client/Util/HttpUtil.cs index 03946a9..bb32cc2 100644 --- a/Client/Util/HttpUtil.cs +++ b/Client/Util/HttpUtil.cs @@ -1,20 +1,22 @@ using Common; using System.Net.Http.Json; using Microsoft.JSInterop; +using Microsoft.AspNetCore.Components.WebAssembly.Authentication; namespace Client { public class HttpUtil { private readonly HttpClient _http; - private IJSRuntime _jSRuntime; - public HttpUtil(HttpClient http, IJSRuntime jsRuntime) { + private readonly IJSRuntime _jSRuntime; + public HttpUtil(HttpClient http, IJSRuntime jsRuntime, IServiceProvider serviceProvider) { _http = http; _jSRuntime = jsRuntime; } public async Task PostAsync(string? requestUri, HttpContent? content) { await RefreshToken(); - return await _http.PostAsync($"{requestUri}", content); + return await _http.PostAsync($"{requestUri}", content); } public async Task GetAsync(string? requestUri) { + await RefreshToken(); return await _http.GetAsync($"{requestUri}"); } public async Task PostAsJsonAsync(string? requestUri, object? content) { @@ -25,9 +27,9 @@ public async Task GetFromJsonAsync(string? requestUri) { await RefreshToken(); return await _http.GetFromJsonAsync($"{requestUri}"); } - private async Task RefreshToken() { + public async Task RefreshToken() { AuthResult authResult; - authResult = await _http.GetFromJsonAsync("Login"); + authResult = await _http.GetFromJsonAsync("Login") ?? new AuthResult(); _http.DefaultRequestHeaders.Remove("X-UserRoles"); _http.DefaultRequestHeaders.Add("X-UserRoles", authResult.Token); @@ -36,6 +38,12 @@ private async Task RefreshToken() { _http.DefaultRequestHeaders.Remove("X-CSRF-TOKEN-HEADER"); _http.DefaultRequestHeaders.Add("X-CSRF-TOKEN-HEADER", token); } - + public async Task RefreshToken(Dictionary requestHeaders) { + AuthResult authResult; + authResult = await _http.GetFromJsonAsync("Login") ?? new AuthResult(); + requestHeaders.Add("X-UserRoles", authResult.Token); + var token = await _jSRuntime.InvokeAsync("getCookie", "XSRF-TOKEN"); + requestHeaders.Add("X-CSRF-TOKEN-HEADER", token); + } } } diff --git a/Client/wwwroot/css/app.css b/Client/wwwroot/css/app.css index 68f67e2..b2fb4bc 100644 --- a/Client/wwwroot/css/app.css +++ b/Client/wwwroot/css/app.css @@ -455,3 +455,14 @@ a { .k-notification-group { width: fit-content; } +.loader-size-title { + display: block; + margin-bottom: 10px; +} + +.loader-container { + text-align: center; + width: 100%; + display: inline-table; + padding-top: 10px; +} \ No newline at end of file diff --git a/Client/wwwroot/index.html b/Client/wwwroot/index.html index 7fe288c..3a6e56a 100644 --- a/Client/wwwroot/index.html +++ b/Client/wwwroot/index.html @@ -13,6 +13,7 @@ + @@ -25,15 +26,17 @@ diff --git a/Client/wwwroot/js/download-upload-files.js b/Client/wwwroot/js/download-upload-files.js new file mode 100644 index 0000000..500967d --- /dev/null +++ b/Client/wwwroot/js/download-upload-files.js @@ -0,0 +1,46 @@ +(function () { + // initiates a file download in the browser for files generated in the C# code + window.saveFile = function (bytesBase64, mimeType, fileName) { + var fileUrl = "data:" + mimeType + ";base64," + bytesBase64; + fetch(fileUrl) + .then(response => response.blob()) + .then(blob => { + var link = window.document.createElement("a"); + link.href = window.URL.createObjectURL(blob, { type: mimeType }); + link.download = fileName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }); + } + + // provides file content for files selected in JS to C# without saving on the server + window.getUploadedFile = (inputID) => { + var inputEl = document.getElementById(inputID); + if (inputEl.files.length == 0) { + return ""; + } + else if (inputEl.files[0].size > (4 * 1024 * 1024)) { // 4MB + inputEl.value = ""; + alert("File size too large. Max allowed size is 4MB."); + return ""; + } + else if (inputEl.accept.length && inputEl.accept.indexOf(inputEl.files[0].name.split('.').pop()) < 0) { + inputEl.value = ""; + alert("Allowed file types: " + inputEl.accept); + return ""; + } + const fileReader = new FileReader(); + return new Promise((resolve) => { + fileReader.onloadend = function (e) { + var data = { + fileName: inputEl.files[0].name, + fileData: e.target.result.split('base64,')[1] + }; + resolve(data); + }; + fileReader.readAsDataURL(inputEl.files[0]); + }); + }; + +})(); \ No newline at end of file diff --git a/Data/EF/Repositories/RepositoryBase.cs b/Data/EF/Repositories/RepositoryBase.cs index be2fa59..3e0f32a 100644 --- a/Data/EF/Repositories/RepositoryBase.cs +++ b/Data/EF/Repositories/RepositoryBase.cs @@ -45,16 +45,14 @@ public Task UpdateAsync(T entity) { } public Task UpdateWithPreValidationAsync(DTObase updateRequest, T entity) { - PreValidation(updateRequest,entity); + PreValidation(updateRequest, entity); return UpdateAsync(entity); } - public virtual void PreValidation(DTObase updateRequest,T entity) { - if(entity is IRowVersionContract updateEntity) { - if (!(entity == null && updateRequest.RowVersion == null) && !updateEntity.RowVersion.SequenceEqual(updateRequest.RowVersion)) { - throw new Exception("Record has already been updated or deleted by another user."); - } + public virtual void PreValidation(DTObase updateRequest, T entity) { + if (entity is IRowVersionContract updateEntity && updateRequest.RowVersion != null && !updateEntity.RowVersion.SequenceEqual(updateRequest.RowVersion)) { + throw new Exception("Record has already been updated or deleted by another user."); } - + } } } \ No newline at end of file diff --git a/DocumentProcessing/Base/DataConverter.cs b/DocumentProcessing/Base/DataConverter.cs new file mode 100644 index 0000000..c45868d --- /dev/null +++ b/DocumentProcessing/Base/DataConverter.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace DocumentProcessing { + public static class DataConverter { + public static byte[] StreamToByte(MemoryStream stream) { + return stream.ToArray(); + } + } +} diff --git a/DocumentProcessing/Base/FontsProvider.cs b/DocumentProcessing/Base/FontsProvider.cs new file mode 100644 index 0000000..55a689c --- /dev/null +++ b/DocumentProcessing/Base/FontsProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Telerik.Documents.Core.Fonts; +using Telerik.Windows.Documents.Core.Fonts; +using Telerik.Windows.Documents.Extensibility; + +namespace DocumentProcessing { + internal class FontsProvider : FontsProviderBase { + private readonly string fontFolder = Environment.GetFolderPath(Environment.SpecialFolder.Fonts); + + public override byte[]? GetFontData(FontProperties fontProperties) { + string fontFamilyName = fontProperties.FontFamilyName; + bool isItalic = fontProperties.FontStyle == FontStyles.Italic; + bool isBold = fontProperties.FontWeight == FontWeights.Bold; + + if (fontFamilyName == "Algerian") { + return this.GetFontDataFromFontFolder("ALGER.TTF"); + } else if (fontFamilyName == "Arial") { + return this.GetFontDataFromFontFolder("arial.ttf"); + } else if (fontFamilyName == "Calibri" && isItalic && isBold) { + return this.GetFontDataFromFontFolder("calibriz.ttf"); + } else if (fontFamilyName == "Calibri") { + return this.GetFontDataFromFontFolder("calibri.ttf"); + } else if (fontFamilyName == "Consolas" && isBold) { + return this.GetFontDataFromFontFolder("consolaz.ttf"); + } else if (fontFamilyName == "Consolas") { + return this.GetFontDataFromFontFolder("consola.ttf"); + } else if (fontFamilyName == "Lucida Calligraphy") { + return this.GetFontDataFromFontFolder("LCALLIG.TTF"); + } else if (fontFamilyName == "Malgun Gothic") { + return this.GetFontDataFromFontFolder("malgun.ttf"); + } else if (fontFamilyName == "Trebuchet MS") { + return this.GetFontDataFromFontFolder("trebuc.ttf"); + } + + return null; + } + + private byte[] GetFontDataFromFontFolder(string fontFileName) { + using (FileStream fileStream = File.OpenRead(this.fontFolder + "\\" + fontFileName)) { + using (MemoryStream memoryStream = new MemoryStream()) { + fileStream.CopyTo(memoryStream); + return memoryStream.ToArray(); + } + } + } + } +} diff --git a/DocumentProcessing/Excel/ISpreadProcessing.cs b/DocumentProcessing/Excel/ISpreadProcessing.cs new file mode 100644 index 0000000..dd2eb4c --- /dev/null +++ b/DocumentProcessing/Excel/ISpreadProcessing.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DocumentProcessing { + public interface ISpreadProcessing { + void ExportToXlsx(string filePath, string resultFile, object workbook); + byte[]? GetXlsxByte(object workbook); + } +} diff --git a/DocumentProcessing/Excel/SpreadProcessing.cs b/DocumentProcessing/Excel/SpreadProcessing.cs new file mode 100644 index 0000000..355b177 --- /dev/null +++ b/DocumentProcessing/Excel/SpreadProcessing.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using Telerik.Windows.Documents.Spreadsheet.FormatProviders; +using Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml.Xlsx; +using Telerik.Windows.Documents.Spreadsheet.Model; + +namespace DocumentProcessing { + public class SpreadProcessing: ISpreadProcessing { + public SpreadProcessing() { + } + public void ExportToXlsx(string filePath, string resultFile, object workbook) { + if (workbook is Workbook telerikWook) { + IWorkbookFormatProvider formatProvider = new XlsxFormatProvider(); + string path = Path.Combine(filePath,resultFile); + using (FileStream stream = File.OpenWrite(path)) { + formatProvider.Export(telerikWook, stream); + } + } + + } + public byte[]? GetXlsxByte(object workbook) { + if (workbook is Workbook telerikWook) { + IWorkbookFormatProvider formatProvider = new XlsxFormatProvider(); + using MemoryStream stream = new MemoryStream(); + formatProvider.Export(telerikWook, stream); + var fileData = DataConverter.StreamToByte(stream); + return fileData; + } + return null; + } + } +} diff --git a/DocumentProcessing/P8.DocumentProcessing.csproj b/DocumentProcessing/P8.DocumentProcessing.csproj new file mode 100644 index 0000000..89db16c --- /dev/null +++ b/DocumentProcessing/P8.DocumentProcessing.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.1 + enable + + + + + + + + + + + + + diff --git a/DocumentProcessing/PDF/IPdfProcessing.cs b/DocumentProcessing/PDF/IPdfProcessing.cs new file mode 100644 index 0000000..061de6d --- /dev/null +++ b/DocumentProcessing/PDF/IPdfProcessing.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace DocumentProcessing { + public interface IPdfProcessing { + void ExportToPDF(string filePath, string fileName, object generatedDocument); + void AddWaterMark(object generatedDocument, WaterMark waterMark); + void MergePDF(string resultFile, List documentList); + byte[] GetPDFByte(object generatedDocument); + byte[] MergePDFToByte(List documentList); + } +} diff --git a/DocumentProcessing/PDF/PdfProcessing.cs b/DocumentProcessing/PDF/PdfProcessing.cs new file mode 100644 index 0000000..9002c79 --- /dev/null +++ b/DocumentProcessing/PDF/PdfProcessing.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using Telerik.Documents.Core.Fonts; +using Telerik.Documents.Primitives; +using Telerik.Windows.Documents.Extensibility; +using Telerik.Windows.Documents.Fixed.FormatProviders.Pdf; +using Telerik.Windows.Documents.Fixed.FormatProviders.Pdf.Export; +using Telerik.Windows.Documents.Fixed.FormatProviders.Pdf.Streaming; +using Telerik.Windows.Documents.Fixed.Model; +using Telerik.Windows.Documents.Fixed.Model.ColorSpaces; +using Telerik.Windows.Documents.Fixed.Model.Editing; +using Telerik.Windows.Documents.Flow.Model; +using Telerik.Windows.Documents.Flow.Model.Editing; + +namespace DocumentProcessing { + public class WaterMark { + public string Text { get; set; } + public byte Transparency { get; set; } + public int Angle { get; set; } + } + public class PdfProcessing : IPdfProcessing { + public PdfProcessing() { + + } + public void ExportToPDF(string filePath, string fileName, object generatedDocument) { + if (generatedDocument == null) { + throw new ArgumentNullException(nameof(generatedDocument)); + } + FontsProviderBase fontsProvider = new FontsProvider(); + FixedExtensibilityManager.FontsProvider = fontsProvider; + PdfFormatProvider formatProvider = new PdfFormatProvider(); + formatProvider.ExportSettings.ImageQuality = ImageQuality.High; + var resultPath=Path.Combine(filePath,fileName); + using (FileStream stream = File.OpenWrite(resultPath)) { + RadFixedDocument? document = generatedDocument as RadFixedDocument; + formatProvider.Export(document, stream); + } + } + public byte[] GetPDFByte(object generatedDocument) { + if (generatedDocument == null) { + throw new ArgumentNullException(nameof(generatedDocument)); + } + FontsProviderBase fontsProvider = new FontsProvider(); + FixedExtensibilityManager.FontsProvider = fontsProvider; + PdfFormatProvider formatProvider = new PdfFormatProvider(); + formatProvider.ExportSettings.ImageQuality = ImageQuality.High; + using MemoryStream stream = new MemoryStream(); + RadFixedDocument? document = generatedDocument as RadFixedDocument; + formatProvider.Export(document, stream); + var fileData = DataConverter.StreamToByte(stream); + return fileData; + + } + public void AddWaterMark(object generatedDocument, WaterMark waterMark) { + if (generatedDocument == null) { + throw new ArgumentNullException(nameof(generatedDocument)); + } + RadFixedDocument? document = generatedDocument as RadFixedDocument; + if (document == null) { + return; + } + foreach (RadFixedPage page in document.Pages) { + AddWatermarkText(page, waterMark); + } + + } + public void MergePDF(string resultFile, List documentList) { + if (File.Exists(resultFile)) { + File.Delete(resultFile); + } + + using (PdfStreamWriter fileWriter = new PdfStreamWriter(File.OpenWrite(resultFile))) { + foreach (RadFixedDocument document in documentList) { + foreach (RadFixedPage page in document.Pages) { + fileWriter.WritePage(page); + } + + } + } + } + public byte[] MergePDFToByte(List documentList) { + using MemoryStream stream = new MemoryStream(); + RadFixedDocument mergeDocument = new RadFixedDocument(); + PdfFormatProvider formatProvider = new PdfFormatProvider(); + formatProvider.ExportSettings.ImageQuality = ImageQuality.High; + foreach (RadFixedDocument document in documentList) { + mergeDocument.Merge(document); + } + formatProvider.Export(mergeDocument, stream); + var fileData = DataConverter.StreamToByte(stream); + return fileData; + } + private void AddWatermarkText(RadFixedPage page, WaterMark waterMark) { + FixedContentEditor editor = new FixedContentEditor(page); + + Block block = new Block(); + block.TextProperties.FontSize = 80; + block.TextProperties.TrySetFont(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Bold); + block.HorizontalAlignment = Telerik.Windows.Documents.Fixed.Model.Editing.Flow.HorizontalAlignment.Center; + block.GraphicProperties.FillColor = new RgbColor(waterMark.Transparency, 255, 0, 0); + block.InsertText(waterMark.Text); + + double angle = waterMark.Angle; + editor.Position.Rotate(angle); + editor.Position.Translate(0, page.Size.Width); + editor.DrawBlock(block, new Size(page.Size.Width / Math.Abs(Math.Sin(angle)), double.MaxValue)); + } + } +} diff --git a/DocumentProcessing/Word/IWordProcessing.cs b/DocumentProcessing/Word/IWordProcessing.cs new file mode 100644 index 0000000..4359791 --- /dev/null +++ b/DocumentProcessing/Word/IWordProcessing.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DocumentProcessing { + public interface IWordProcessing { + void ExportToWord(string filePath, string resultFile, DocumentFormat documentFormat, object generatedDocument); + byte[]? GetWordByte(DocumentFormat documentFormat, object generatedDocument); + } +} diff --git a/DocumentProcessing/Word/WordProcessing.cs b/DocumentProcessing/Word/WordProcessing.cs new file mode 100644 index 0000000..ca6bd4f --- /dev/null +++ b/DocumentProcessing/Word/WordProcessing.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Telerik.Windows.Documents.Extensibility; +using Telerik.Windows.Documents.Fixed.FormatProviders.Pdf; +using Telerik.Windows.Documents.Flow.FormatProviders.Docx; +using Telerik.Windows.Documents.Flow.FormatProviders.Html; +using Telerik.Windows.Documents.Flow.FormatProviders.Rtf; +using Telerik.Windows.Documents.Flow.FormatProviders.Txt; +using Telerik.Windows.Documents.Flow.Model; +using Telerik.Windows.Documents.Common.FormatProviders; +using System.IO; +using System.Diagnostics; +using Telerik.Windows.Documents.Fixed.Model; + +namespace DocumentProcessing { + public enum DocumentFormat { + docx=1, + html=2, + rtf=3, + txt=4, + pdf=5 + } + public class WordProcessing : IWordProcessing { + public void ExportToWord(string filePath, string resultFile, DocumentFormat documentFormat, object generatedDocument) { + if (generatedDocument is RadFlowDocument flowDocument) { + string selectedFormat = documentFormat.ToString(); + IFormatProvider formatProvider = FormatDocumentType(selectedFormat); + string path = Path.Combine(filePath, $"{resultFile}.{selectedFormat}"); + using (FileStream stream = File.OpenWrite(path)) { + formatProvider.Export(flowDocument, stream); + } + + } + } + public byte[]? GetWordByte(DocumentFormat documentFormat, object generatedDocument) { + if (generatedDocument is RadFlowDocument flowDocument) { + string selectedFormat = documentFormat.ToString(); + IFormatProvider formatProvider = FormatDocumentType(selectedFormat); + using MemoryStream stream = new MemoryStream(); + formatProvider.Export(flowDocument, stream); + var fileData = DataConverter.StreamToByte(stream); + return fileData; + } + return null; + } + + private static IFormatProvider FormatDocumentType(string selectedFormat) { + FontsProviderBase fontsProvider = new FontsProvider(); + FixedExtensibilityManager.FontsProvider = fontsProvider; + + IFormatProvider formatProvider; + switch (selectedFormat) { + case "docx": + formatProvider = new DocxFormatProvider(); + break; + case "rtf": + formatProvider = new RtfFormatProvider(); + break; + case "txt": + formatProvider = new TxtFormatProvider(); + break; + case "html": + formatProvider = new HtmlFormatProvider(); + break; + default: + formatProvider = new DocxFormatProvider(); + break; + } + + return formatProvider; + } + } +} diff --git a/DocumentProcessing/Zip/IZipProcessing.cs b/DocumentProcessing/Zip/IZipProcessing.cs new file mode 100644 index 0000000..5a83454 --- /dev/null +++ b/DocumentProcessing/Zip/IZipProcessing.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Telerik.Zip; + +namespace DocumentProcessing { + public interface IZipProcessing { + void CreateZip(string zipFileName, string[] zipArchiveFiles); + void CreateZip(Stream stream, string[] zipArchiveFiles); + void CreateZip(string zipFileName, Dictionary zipArchiveFiles); + void CreateZip(string zipFileName, Dictionary zipArchiveFiles, Encoding? entryNameEncoding, CompressionSettings? compressionSettings, EncryptionSettings? encryptionSettings); + byte[] GetZipBytes(MemoryStream stream, string[] zipArchiveFiles); + byte[] GetZipBytes(Dictionary zipArchiveFiles, Encoding? entryNameEncoding, CompressionSettings? compressionSettings, EncryptionSettings? encryptionSettings); + } +} diff --git a/DocumentProcessing/Zip/ZipProcessing.cs b/DocumentProcessing/Zip/ZipProcessing.cs new file mode 100644 index 0000000..0f1f088 --- /dev/null +++ b/DocumentProcessing/Zip/ZipProcessing.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using Telerik.Zip; + +namespace DocumentProcessing { + public class ZipProcessing : IZipProcessing { + public ZipProcessing() { + } + public void CreateZip(string zipFileName, string[] zipArchiveFiles) { + using (Stream stream = File.Open(zipFileName, FileMode.Create)) { + CreateZip(stream, zipArchiveFiles); + } + } + public void CreateZip(Stream stream, string[] zipArchiveFiles) { + using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Create, false, null)) { + foreach (string file in zipArchiveFiles) { + string sourceFileName = file; + string fileName = file.Split(new string[] { @"\" }, StringSplitOptions.None).Last(); + ZipArchiveEntry entry; + using (entry = archive.CreateEntry(fileName)) { + using (Stream fileStream = File.Open(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { + // Setting the ExternalAttributes property + entry.ExternalAttributes = (int)File.GetAttributes(sourceFileName); + DateTime lastWriteTime = File.GetLastWriteTime(sourceFileName); + // Setting the LastWriteTime property + entry.LastWriteTime = lastWriteTime; + using (Stream entryStream = entry.Open()) { + fileStream.CopyTo(entryStream); + } + } + } + } + } + } + public byte[] GetZipBytes(MemoryStream stream, string[] zipArchiveFiles) { + CreateZip(stream, zipArchiveFiles); + var fileData = DataConverter.StreamToByte(stream); + return fileData; + } + public void CreateZip(string zipFileName, Dictionary zipArchiveFiles) { + CreateZip(zipFileName, zipArchiveFiles, entryNameEncoding: null, compressionSettings: null, encryptionSettings: null); + } + public void CreateZip(string zipFileName, Dictionary zipArchiveFiles, Encoding? entryNameEncoding, CompressionSettings? compressionSettings, EncryptionSettings? encryptionSettings) { + using (Stream stream = File.Open(zipFileName, FileMode.Create)) { + using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Create, false, entryNameEncoding, compressionSettings, encryptionSettings)) { + foreach (var file in zipArchiveFiles) { + ZipArchiveEntry entry; + using (entry = archive.CreateEntry(file.Key)) { + using (Stream entryStream = entry.Open()) { + file.Value.CopyTo(entryStream); + } + } + } + } + } + } + public byte[] GetZipBytes(Dictionary zipArchiveFiles, Encoding? entryNameEncoding, CompressionSettings? compressionSettings, EncryptionSettings? encryptionSettings) { + using (MemoryStream stream = new MemoryStream()) { + using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Create, false, entryNameEncoding, compressionSettings, encryptionSettings)) { + foreach (var file in zipArchiveFiles) { + ZipArchiveEntry entry; + using (entry = archive.CreateEntry(file.Key)) { + using (Stream entryStream = entry.Open()) { + file.Value.CopyTo(entryStream); + } + } + } + } + var fileData = DataConverter.StreamToByte(stream); + return fileData; + } + } + } +} diff --git a/StoryTest/Support/TestHelper.cs b/StoryTest/Support/TestHelper.cs index 5d74c34..5bd265d 100644 --- a/StoryTest/Support/TestHelper.cs +++ b/StoryTest/Support/TestHelper.cs @@ -45,6 +45,7 @@ public static T SetDTO(Table table, string varName) where T : class { public async static Task PostAPI(string vNameDTO, string apiRoute, string vNameResponse) where T : class { T request = context.Get(vNameDTO); + await RefreshToken(); var response = await client.PostAsJsonAsync(apiRoute, request); context.Set(response, vNameResponse); } @@ -85,6 +86,7 @@ public static bool Compare(Table table, T dto) where T : class { return match; } public async static Task GetAPI(string apiRoute, string vNameDTO) where T : class { + await RefreshToken(); var response = await client.GetAsync(apiRoute); var content = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { @@ -107,5 +109,14 @@ public static bool CompareList(Table table, T dto, string varName) where T } return anymatch; } + + private async static Task RefreshToken() + { + var testResult = await client.GetAsync("Login"); + var cookies = testResult.Headers.GetValues("Set-Cookie").ToList(); + var token = cookies.Single(x => x.StartsWith("XSRF-TOKEN"))?.Substring($"{"XSRF-TOKEN"}=".Length).Split(";")[0]; + client.DefaultRequestHeaders.Remove("X-CSRF-TOKEN-HEADER"); + client.DefaultRequestHeaders.Add("X-CSRF-TOKEN-HEADER", token); + } } } diff --git a/dotnetEAA.sln b/dotnetEAA.sln index 40ae1d6..d3eea0c 100644 --- a/dotnetEAA.sln +++ b/dotnetEAA.sln @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "P7.Migrator", "Migrator\P7.Migrator.csproj", "{DBF08A21-86ED-458C-875E-9B5858713333}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "P8.DocumentProcessing", "DocumentProcessing\P8.DocumentProcessing.csproj", "{DD3091CB-35FF-459F-A094-152B4F0BD734}" +EndProject # Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "X1.Messages", "ServiceBusDemo\Messages\X1.Messages.csproj", "{FD97222E-1A12-47BE-90B6-03D7AB7A4BFB}" # EndProject # Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "X2.Banking", "ServiceBusDemo\Banking\X2.Banking.csproj", "{5A33E931-F79A-4C74-AA02-CAEE7D8BF024}" @@ -85,6 +87,12 @@ Global {DBF08A21-86ED-458C-875E-9B5858713333}.Release|Any CPU.Build.0 = Release|Any CPU {DBF08A21-86ED-458C-875E-9B5858713333}.SpecFlow|Any CPU.ActiveCfg = SpecFlow|Any CPU {DBF08A21-86ED-458C-875E-9B5858713333}.SpecFlow|Any CPU.Build.0 = SpecFlow|Any CPU + {DD3091CB-35FF-459F-A094-152B4F0BD734}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD3091CB-35FF-459F-A094-152B4F0BD734}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD3091CB-35FF-459F-A094-152B4F0BD734}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD3091CB-35FF-459F-A094-152B4F0BD734}.Release|Any CPU.Build.0 = Release|Any CPU + {DD3091CB-35FF-459F-A094-152B4F0BD734}.SpecFlow|Any CPU.ActiveCfg = Debug|Any CPU + {DD3091CB-35FF-459F-A094-152B4F0BD734}.SpecFlow|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE