diff --git a/src/EPPlus.Export.Pdf/AssemblyInfo.cs b/src/EPPlus.Export.Pdf/AssemblyInfo.cs index d79820d85e..adeb5b8146 100644 --- a/src/EPPlus.Export.Pdf/AssemblyInfo.cs +++ b/src/EPPlus.Export.Pdf/AssemblyInfo.cs @@ -28,4 +28,6 @@ Date Author Change // The following GUID is for the ID of the typelib if this project is exposed to COM. [assembly: Guid("60855b7d-19da-4dfa-90b2-78231d227d65")] -[assembly: InternalsVisibleTo("EPPlus.Export.Pdf.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd3a3466a88cbf5d374fe992cec433c48022414fe96608933e8e36782001213dd31bc454dc6f962a54a3a76cfb9e03a32cd4c658ecd49d1a98709971a080ab92d5c5b65346155f8d6422db4ffbf662f78913996a9a8b78ee11ff3cda7e585208cd4468fb3201f15bbb1dfc45c120703c9d6ad495bb9de66893ae5ab5ac8f40dc")] \ No newline at end of file +[assembly: InternalsVisibleTo("EPPlus.Export.Pdf.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd3a3466a88cbf5d374fe992cec433c48022414fe96608933e8e36782001213dd31bc454dc6f962a54a3a76cfb9e03a32cd4c658ecd49d1a98709971a080ab92d5c5b65346155f8d6422db4ffbf662f78913996a9a8b78ee11ff3cda7e585208cd4468fb3201f15bbb1dfc45c120703c9d6ad495bb9de66893ae5ab5ac8f40dc")] +// +[assembly: InternalsVisibleTo("EPPlus.PdfExportPerformance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f5398d389a44aacee243c9e73152fae942e9ede958ebd4b06651a749e6b2c0395a66bc0fc133dce1cf83cd667fe8f761b433063e603968f12b0b17e474233410f8fe933853d2344e89526414ef65ed7236a7a5c012e30275ec30d7fb665dbe1bd3435439bec55f431b9e69e943294fd474942d6bcb431c94ef653cbeee5b8a9e")] \ No newline at end of file diff --git a/src/EPPlus.Export.Pdf/ExcelPdf.cs b/src/EPPlus.Export.Pdf/ExcelPdf.cs index 65f34e8a1b..3ce420925c 100644 --- a/src/EPPlus.Export.Pdf/ExcelPdf.cs +++ b/src/EPPlus.Export.Pdf/ExcelPdf.cs @@ -34,10 +34,12 @@ public class ExcelPdf { internal List _workheets = new List(); internal ExcelRangeBase _range; - private PdfPageSettings PageSettings; - internal List Document = new List(); + private PdfPageSettings _pageSettings; + internal List _document = new List(); internal string header = "%PDF-1.7\n"; - internal PdfDictionaries Dictionaries = new PdfDictionaries(); + internal PdfDictionaries _dictionaries = new PdfDictionaries(); + private string _debugString; + public ExcelPdf() { } @@ -50,8 +52,8 @@ public ExcelPdf() public ExcelPdf(ExcelWorksheet worksheet, PdfPageSettings pageSettings = null) { _workheets.Add(worksheet); - PageSettings = pageSettings == null ? new PdfPageSettings() : pageSettings; - PageSettings.defaultFontName = worksheet.Workbook.ThemeManager.CurrentTheme.FontScheme.MinorFont[0].Typeface; + _pageSettings = pageSettings == null ? new PdfPageSettings() : pageSettings; + _pageSettings.defaultFontName = worksheet.Workbook.ThemeManager.CurrentTheme.FontScheme.MinorFont[0].Typeface; } /// @@ -96,9 +98,9 @@ internal string GetPatternLabel(PdfCellLayout layout) if ((layout.CellFillData.PatternStyle != ExcelFillStyle.Solid && layout.CellFillData.PatternStyle != ExcelFillStyle.None) || layout.CellFillData.GradientFillData != null) { var patternName = layout.CellFillData.id; - if (Dictionaries.Patterns.ContainsKey(patternName)) + if (_dictionaries.Patterns.ContainsKey(patternName)) { - return Dictionaries.Patterns[patternName].Label; + return _dictionaries.Patterns[patternName].Label; } } return null; @@ -107,28 +109,28 @@ internal string GetPatternLabel(PdfCellLayout layout) //Add Fonts //Need to update this method a bit. We should check for all default fonts and not only courier new? Also need to check if we are allowed to embedd the font. internal void AddFontData() { - if (PageSettings.EmbeddFonts) + if (_pageSettings.EmbeddFonts) { - foreach (var font in Dictionaries.Fonts) + foreach (var font in _dictionaries.Fonts) { //font.Value.CreateGidsAndCharMaps(); - var CidSet = font.Value.GetCidSet(Document.Count + 1); - if (CidSet != null) Document.Add(CidSet); - Document.Add(font.Value.GetEmbeddedFontStreamObject(Document.Count + 1)); - Document.Add(font.Value.GetFontDescriptorObject(Document.Count + 1)); - Document.Add(font.Value.GetCIDFontObject(Document.Count + 1)); - Document.Add(font.Value.GetUnicodeCmapObject(Document.Count + 1)); - Document.Add(font.Value.GetType0FontDictObject(Document.Count + 1)); - font.Value.GetFontObject(Document.Count); + var cidSet = font.Value.GetCidSet(_document.Count + 1); + if (cidSet != null) _document.Add(cidSet); + _document.Add(font.Value.GetEmbeddedFontStreamObject(_document.Count + 1)); + _document.Add(font.Value.GetFontDescriptorObject(_document.Count + 1)); + _document.Add(font.Value.GetCIDFontObject(_document.Count + 1)); + _document.Add(font.Value.GetUnicodeCmapObject(_document.Count + 1)); + _document.Add(font.Value.GetType0FontDictObject(_document.Count + 1)); + font.Value.GetFontObject(_document.Count); } } else { - foreach (var font in Dictionaries.Fonts) + foreach (var font in _dictionaries.Fonts) { - Document.Add(font.Value.GetFontDescriptorObject(Document.Count + 1)); - Document.Add(font.Value.GetWidthsObject(Document.Count + 1)); - Document.Add(font.Value.GetFontObject(Document.Count + 1)); + _document.Add(font.Value.GetFontDescriptorObject(_document.Count + 1)); + _document.Add(font.Value.GetWidthsObject(_document.Count + 1)); + _document.Add(font.Value.GetFontObject(_document.Count + 1)); } } } @@ -136,47 +138,47 @@ internal void AddFontData() //Add Patterns internal void AddPatternData() { - foreach (var pattern in Dictionaries.Patterns) + foreach (var pattern in _dictionaries.Patterns) { - Document.Add(pattern.Value.GetPatternObject(Document.Count + 1)); + _document.Add(pattern.Value.GetPatternObject(_document.Count + 1)); } } //Add Shadings and accompanying pattern - internal void AddShadingsData(PdfDictionaries dictionaries) + internal void AddShadingsData() { - foreach (var shading in Dictionaries.Shadings) + foreach (var shading in _dictionaries.Shadings) { - Document.Add(shading.Value.GetShadingObject(Document.Count + 1)); - Document.Add(shading.Value.GetShadingPatternObject(Document.Count + 1, Document.Count)); - int label = dictionaries.Patterns.Last().Value.labelNumber + 1; + _document.Add(shading.Value.GetShadingObject(_document.Count + 1)); + _document.Add(shading.Value.GetShadingPatternObject(_document.Count + 1, _document.Count)); + int label = _dictionaries.Patterns.Last().Value.labelNumber + 1; var pr = new PdfPatternResource(label, shading.Value.CellFillData); - pr.objectNumber = Document.Count; - dictionaries.Patterns.Add(shading.Value.CellFillData.id, pr); + pr.objectNumber = _document.Count; + _dictionaries.Patterns.Add(shading.Value.CellFillData.id, pr); } } //Create Page private PdfPage AddPage(int pagesObjectNumber, List contentObjectNumbers, PdfPageSettings settings) { - var page = new PdfPage(Document.Count + 1, pagesObjectNumber, contentObjectNumbers, settings.PageSize, Dictionaries); - Document.Add(page); + var page = new PdfPage(_document.Count + 1, pagesObjectNumber, contentObjectNumbers, settings.PageSize, _dictionaries); + _document.Add(page); return page; } //Create Pages private PdfPages AddPages() { - var pages = new PdfPages(Document.Count + 1, new List{}); - Document.Add(pages); + var pages = new PdfPages(_document.Count + 1, new List { }); + _document.Add(pages); return pages; } //Create Catalog private PdfObjects.PdfCatalog AddCatalog(int pagesObjectNumber) { - var catalog = new PdfObjects.PdfCatalog(Document.Count + 1, pagesObjectNumber); - Document.Add(catalog); + var catalog = new PdfObjects.PdfCatalog(_document.Count + 1, pagesObjectNumber); + _document.Add(catalog); return catalog; } @@ -186,12 +188,12 @@ private void AddContent(Transform pageLayout, PdfPage page) //var cells = pageLayout.ChildObjects.Where(t => t is PdfCellLayout || t is PdfCellContentLayout || t is PdfCellBorderLayout).GroupBy(t => t.Name); var cells = pageLayout.ChildObjects.Where(t => (t is PdfCellLayout || t is PdfCellContentLayout || t is PdfCellBorderLayout) && !(t is PdfCellContentLayout cc && cc.IsHeaderFooter)).GroupBy(t => t.Name); var headerFooterLayouts = pageLayout.ChildObjects.OfType().Where(t => t.IsHeaderFooter); - var contentStream = new PdfContentStream(Document.Count + 1); + var contentStream = new PdfContentStream(_document.Count + 1); contentStream.AddCommand($"% {pageLayout.Name} start"); //Add clipping rectangle around page content. contentStream.AddCommand("q"); contentStream.AddMarginClipping((PdfPageLayout)pageLayout); - if (PageSettings.ShowGridLines) + if (_pageSettings.ShowGridLines) { contentStream.AddInnerGridLines(pageLayout); } @@ -206,7 +208,7 @@ private void AddContent(Transform pageLayout, PdfPage page) contentStream.AddCellLayout(layout, GetPatternLabel(layout)); break; case PdfCellContentLayout contentLayout: - contentStream.AddCellContentLayout(contentLayout, Dictionaries, PageSettings); + contentStream.AddCellContentLayout(contentLayout, _dictionaries, _pageSettings); break; case PdfCellBorderLayout borderLayout: contentStream.AddBorderLayout(borderLayout); @@ -217,16 +219,16 @@ private void AddContent(Transform pageLayout, PdfPage page) //Close the clipping rectangle. contentStream.AddCommand("Q"); contentStream.AddCommand($"% Margin Clip End"); - if (PageSettings.ShowGridLines) + if (_pageSettings.ShowGridLines) { contentStream.AddOuterGridBorder(pageLayout); } //Add header and footer. foreach (var hf in headerFooterLayouts) { - contentStream.AddCellContentLayout(hf, Dictionaries, PageSettings); + contentStream.AddCellContentLayout(hf, _dictionaries, _pageSettings); } - Document.Add(contentStream); + _document.Add(contentStream); page.contentObjectNumbers.Add(contentStream.objectNumber); contentStream.AddCommand($"% {pageLayout.Name} end"); } @@ -238,23 +240,35 @@ private void AddHeaderFooter(PdfContentStream contentStream, Transform pageLayou foreach (var hf in headerFooter) { var headerFooterLayout = hf as PdfHeaderFooterLayout; - contentStream.AddCellContentLayout(headerFooterLayout, Dictionaries, PageSettings); + contentStream.AddCellContentLayout(headerFooterLayout, _dictionaries, _pageSettings); } } //Add Info private PdfInfoObject AddInfoObject(string workBookName = "") { - var info = new PdfInfoObject(Document.Count + 1, workBookName); - Document.Add(info); + var info = new PdfInfoObject(_document.Count + 1, workBookName); + _document.Add(info); return info; } - internal void CreatePdf(PdfPageSettings pageSettings, PdfDictionaries dictionaries, Transform layout, string fileName) { - PageSettings = pageSettings; - Dictionaries = dictionaries; + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + CreatePdf(pageSettings, dictionaries, layout, fs); + } + + if (_pageSettings.Debug && _pageSettings.PrintAsText) + { + WriteDebugText(fileName); + } + } + + internal void CreatePdf(PdfPageSettings pageSettings, PdfDictionaries dictionaries, Transform layout, Stream stream) + { + _pageSettings = pageSettings; + _dictionaries = dictionaries; var catalog = AddCatalog(2); //Create Pages @@ -265,64 +279,45 @@ internal void CreatePdf(PdfPageSettings pageSettings, PdfDictionaries dictionari //Create Patterns AddPatternData(); //Create Shadings - AddShadingsData(dictionaries); + AddShadingsData(); //Create Page and Content for (int i = 0; i < layout.ChildObjects.Count; i++) { var pageLayout = layout.ChildObjects[i]; - var page = AddPage(2, new List(), PageSettings); + var page = AddPage(2, new List(), _pageSettings); AddContent(pageLayout, page); pages.pageObjectNumbers.Add(page.objectNumber); } var info = AddInfoObject(); - string debugString = ""; - //write to pdf - PdfCrossRefTable crossRefTable = new PdfCrossRefTable(); - //start wring pdf binary - using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + + WriteDocumentToStream(stream, catalog, info); + } + + /// + /// Create the pdf from the supplied worksheet. + /// + /// The file name + public void CreatePdf(string Filename) + { + using (var fs = new FileStream(Filename, FileMode.Create, FileAccess.Write)) { - using (var bw = new BinaryWriter(fs, Encoding.ASCII)) - { - //Write header - bw.Write(Encoding.ASCII.GetBytes(header)); - debugString += header; - //Write body - foreach (var pdfobj in Document) - { - crossRefTable.AddPosition(fs.Position); - pdfobj.ToPdfBytes(bw); - debugString += pdfobj.ToPdfString(); - } - //Write CrossReference - crossRefTable.Write(bw, fs.Position, Document.Count); - debugString += crossRefTable.WriteString(Document.Count); - // Write trailer - PdfTrailer.Write(bw, Document.Count, catalog.objectNumber, info.objectNumber, crossRefTable.StartPosition); - debugString += PdfTrailer.WriteString(Document.Count, catalog.objectNumber, info.objectNumber, crossRefTable.StartPosition); - } + CreatePdf(fs); } - //Write pdf as txt for debug. - if (PageSettings.Debug && PageSettings.PrintAsText) + + if (_pageSettings.Debug && _pageSettings.PrintAsText) { - using (var fs = new FileStream(fileName + ".txt", FileMode.Create, FileAccess.Write)) - { - using (var wr = new StreamWriter(fs)) - { - wr.Write(debugString); - } - } + WriteDebugText(Filename); } } - /// - /// Create the pdf from the supplied worksheet. + /// Create the pdf from the supplied worksheet and write it to a stream. /// - /// The file name - public void CreatePdf(string Filename) + /// The stream to write the pdf to. The stream will not be closed. + public void CreatePdf(Stream stream) { //Create Catalog - var catalogLayout = new PdfCatalogLayout(_workheets[0], PageSettings, Dictionaries); + var catalogLayout = new PdfCatalogLayout(_workheets[0], _pageSettings, _dictionaries); var catalog = AddCatalog(2); //Create Pages var pagesLayout = catalogLayout.ChildObjects[0]; @@ -332,53 +327,64 @@ public void CreatePdf(string Filename) //Create Patterns AddPatternData(); //Create Shadings - AddShadingsData(Dictionaries); + AddShadingsData(); //Create Page and Content for (int i = 0; i < pagesLayout.ChildObjects.Count; i++) { var pageLayout = pagesLayout.ChildObjects[i]; - var page = AddPage(2, new List(), PageSettings); + var page = AddPage(2, new List(), _pageSettings); AddContent(pageLayout, page); pages.pageObjectNumbers.Add(page.objectNumber); } var info = AddInfoObject(_workheets[0].Workbook._package.File.Name); - string debugString = ""; - //write to pdf + + WriteDocumentToStream(stream, catalog, info); + } + + //Write the document and cross-ref/trailer to the supplied stream. + //The stream is not closed; the caller owns it. + private void WriteDocumentToStream(Stream stream, PdfObjects.PdfCatalog catalog, PdfInfoObject info) + { + _debugString = ""; PdfCrossRefTable crossRefTable = new PdfCrossRefTable(); - //start wring pdf binary - using (var fs = new FileStream(Filename, FileMode.Create, FileAccess.Write)) + + //Use a BinaryWriter without disposing it, so the underlying stream stays open for the caller. + //BinaryWriter does not own the stream when we don't dispose it; we just flush at the end. + var bw = new BinaryWriter(stream, Encoding.ASCII); + try { - using (var bw = new BinaryWriter(fs, Encoding.ASCII)) + //Write header + bw.Write(Encoding.ASCII.GetBytes(header)); + _debugString += header; + //Write body + foreach (var pdfobj in _document) { - //Write header - bw.Write(Encoding.ASCII.GetBytes(header)); - debugString += header; - //Write body - foreach (var pdfobj in Document) - { - crossRefTable.AddPosition(fs.Position); - pdfobj.ToPdfBytes(bw); - debugString += pdfobj.ToPdfString(); - } - //Write CrossReference - crossRefTable.Write(bw, fs.Position, Document.Count); - debugString += crossRefTable.WriteString(Document.Count); - // Write trailer - PdfTrailer.Write(bw, Document.Count, catalog.objectNumber, info.objectNumber, crossRefTable.StartPosition); - debugString += PdfTrailer.WriteString(Document.Count, catalog.objectNumber, info.objectNumber, crossRefTable.StartPosition); + crossRefTable.AddPosition(stream.Position); + pdfobj.ToPdfBytes(bw); + _debugString += pdfobj.ToPdfString(); } + //Write CrossReference + crossRefTable.Write(bw, stream.Position, _document.Count); + _debugString += crossRefTable.WriteString(_document.Count); + // Write trailer + PdfTrailer.Write(bw, _document.Count, catalog.objectNumber, info.objectNumber, crossRefTable.StartPosition); + _debugString += PdfTrailer.WriteString(_document.Count, catalog.objectNumber, info.objectNumber, crossRefTable.StartPosition); + } + finally + { + bw.Flush(); } - //Write pdf as txt for debug. - if (PageSettings.Debug && PageSettings.PrintAsText) + } + + private void WriteDebugText(string fileName) + { + using (var fs = new FileStream(fileName + ".txt", FileMode.Create, FileAccess.Write)) { - using (var fs = new FileStream(Filename + ".txt", FileMode.Create, FileAccess.Write)) + using (var wr = new StreamWriter(fs)) { - using ( var wr = new StreamWriter(fs)) - { - wr.Write(debugString); - } + wr.Write(_debugString); } } } } -} +} \ No newline at end of file diff --git a/src/EPPlus.Export.Pdf/PdfCatalog/PdfCatalog.cs b/src/EPPlus.Export.Pdf/PdfCatalog/PdfCatalog.cs index 052abc1a73..75c517d086 100644 --- a/src/EPPlus.Export.Pdf/PdfCatalog/PdfCatalog.cs +++ b/src/EPPlus.Export.Pdf/PdfCatalog/PdfCatalog.cs @@ -2,16 +2,18 @@ using EPPlus.Export.Pdf.PdfSettings; using EPPlus.Graphics; using OfficeOpenXml; +using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; namespace EPPlus.Export.Pdf.PdfCatalog { - internal class PdfCatalog + public class PdfCatalog { - internal PdfDictionaries Dictionaries = new PdfDictionaries(); - private bool AddTextForHeadings = true; + internal PdfDictionaries _dictionaries = new PdfDictionaries(); + private bool _addTextForHeadings = true; //Constructors public PdfCatalog() { } @@ -41,45 +43,71 @@ private void HandleWorksheetCollection(PdfPageSettings pageSettings, ExcelWorksh } public PdfCatalog(PdfPageSettings pageSettings, ExcelWorksheet worksheet, string fileName) + { + BuildPdf(pageSettings, worksheet, (excelPdf, layout) => + excelPdf.CreatePdf(pageSettings, _dictionaries, layout, fileName)); + } + + public PdfCatalog(PdfPageSettings pageSettings, ExcelWorksheet worksheet, Stream stream) + { + BuildPdf(pageSettings, worksheet, (excelPdf, layout) => + excelPdf.CreatePdf(pageSettings, _dictionaries, layout, stream)); + } + + private void BuildPdf(PdfPageSettings pageSettings, ExcelWorksheet worksheet, Action writePdf) { pageSettings.defaultFontName = worksheet.Workbook.ThemeManager.CurrentTheme.FontScheme.MinorFont[0].Typeface; - Stopwatch sw = Stopwatch.StartNew(); + PdfWorksheet pdfSheet = null; + try + { + Stopwatch sw = Stopwatch.StartNew(); - //Collect Text - PdfWorksheet pdfSheet = GetPdfWorksheet(pageSettings, worksheet); - sw.Stop(); - var CollectTextTime = sw.ElapsedMilliseconds; - sw.Reset(); - sw.Start(); + //Collect Text + pdfSheet = GetPdfWorksheet(pageSettings, worksheet); + sw.Stop(); + var CollectTextTime = sw.ElapsedMilliseconds; + sw.Reset(); + sw.Start(); - //Shape Text - ShapeTextInPdfWorksheet(pageSettings, pdfSheet); - sw.Stop(); - var ShapeTextTime = sw.ElapsedMilliseconds; - sw.Reset(); - sw.Start(); - - //Auto-Fit Rows - PdfCalculateRowHeight.ResizeRowHeights(pdfSheet); //call a method that does this so we can use it for comments sheet aswell! - sw.Stop(); - var AutoFitRowTime = sw.ElapsedMilliseconds; - sw.Reset(); - sw.Start(); - - //Create Layout - var Layout = GetLayout(pageSettings, pdfSheet); - sw.Stop(); - var CreateLayoutTime = sw.ElapsedMilliseconds; - sw.Reset(); - sw.Start(); - - //Create Pdf - ExcelPdf excelPdf = new ExcelPdf(); - excelPdf.CreatePdf(pageSettings, Dictionaries, Layout, fileName); - sw.Stop(); - var CreatePdfTime = sw.ElapsedMilliseconds; - sw.Reset(); + //Shape Text + ShapeTextInPdfWorksheet(pageSettings, pdfSheet); + sw.Stop(); + var ShapeTextTime = sw.ElapsedMilliseconds; + sw.Reset(); + sw.Start(); + + //Auto-Fit Rows + PdfCalculateRowHeight.ResizeRowHeights(pdfSheet); + sw.Stop(); + var AutoFitRowTime = sw.ElapsedMilliseconds; + sw.Reset(); + sw.Start(); + + //Create Layout + var Layout = GetLayout(pageSettings, pdfSheet); + sw.Stop(); + var CreateLayoutTime = sw.ElapsedMilliseconds; + sw.Reset(); + sw.Start(); + + //Create Pdf + ExcelPdf excelPdf = new ExcelPdf(); + writePdf(excelPdf, Layout); + sw.Stop(); + var CreatePdfTime = sw.ElapsedMilliseconds; + sw.Reset(); + } + finally + { + //Clean up the temporary worksheet used to build the comments/notes pages, + //so the source workbook isn't permanently mutated by the PDF export. + if (pdfSheet != null && pdfSheet.CommentsAndNotesSheet != null) + { + worksheet.Workbook.Worksheets.Delete(pdfSheet.CommentsAndNotesSheet); + pdfSheet.CommentsAndNotesSheet = null; + } + } } public PdfCatalog(PdfPageSettings pageSettings, ExcelRangeBase range) @@ -88,7 +116,7 @@ public PdfCatalog(PdfPageSettings pageSettings, ExcelRangeBase range) ShapeTextInPdfWorksheet(pageSettings, pdfSheet); } - public PdfCellCollection GetCellCollectionFromRange(PdfPageSettings pageSettings, ExcelRangeBase range) + internal PdfCellCollection GetCellCollectionFromRange(PdfPageSettings pageSettings, ExcelRangeBase range) { PdfWorksheet pdfSheet = GetPdfWorksheet(pageSettings, range); ShapeTextInPdfWorksheet(pageSettings, pdfSheet); @@ -99,13 +127,28 @@ public PdfCellCollection GetCellCollectionFromRange(PdfPageSettings pageSettings //Create Layout Methods private Transform GetLayout(PdfPageSettings pageSettings, PdfWorksheet pdfSheet) { - PdfWorksheet[] pdfSheets = new PdfWorksheet[1]{ pdfSheet }; - var Layout = PdfLayout.GetLayout(pageSettings, Dictionaries, pdfSheets); + PdfWorksheet[] pdfSheets = new PdfWorksheet[1] { pdfSheet }; + var Layout = PdfLayout.GetLayout(pageSettings, _dictionaries, pdfSheets); return Layout; } //Shape Text Methods - private void ShapeTextInPdfWorksheet(PdfPageSettings pageSettings, PdfWorksheet pdfSheet) + internal void ShapeTextInPdfWorksheet(PdfPageSettings pageSettings, PdfWorksheet pdfSheet) + { + // Pass 1: collect text per font + IterateCells(pdfSheet, cell => PdfTextShaper.CollectText(_dictionaries, cell)); + + // Pass 2: build one provider per font + foreach (var kvp in _dictionaries.Fonts) + { + _dictionaries.ShapedProviders[kvp.Key] = kvp.Value.fontSubsetManager.CreateSubsettedProvider(); + } + + // Pass 3: shape text using the pre-built providers + IterateCells(pdfSheet, cell => PdfTextShaper.ShapeText(pageSettings, _dictionaries, cell)); + } + + private void IterateCells(PdfWorksheet pdfSheet, System.Action action) { foreach (var range in pdfSheet.Ranges) { @@ -113,29 +156,21 @@ private void ShapeTextInPdfWorksheet(PdfPageSettings pageSettings, PdfWorksheet { for (int j = range.Map.FromColumn; j <= range.Map.ToColumn; j++) { - var cell = range.Map[i, j]; - PdfTextShaper.LayoutAndShapeText(pageSettings, Dictionaries, cell); + action(range.Map[i, j]); } } } + if (pdfSheet.CommentsAndNotes.Map != null) { for (int i = pdfSheet.CommentsAndNotes.Map.FromRow; i <= pdfSheet.CommentsAndNotes.Map.ToRow; i++) { for (int j = pdfSheet.CommentsAndNotes.Map.FromColumn; j <= pdfSheet.CommentsAndNotes.Map.ToColumn; j++) { - var cell = pdfSheet.CommentsAndNotes.Map[i, j]; - PdfTextShaper.LayoutAndShapeText(pageSettings, Dictionaries, cell); + action(pdfSheet.CommentsAndNotes.Map[i, j]); } } } - //if (pdfSheet.HeaderFooters != null) - //{ - // foreach (var hf in pdfSheet.HeaderFooters.PdfHeaderFooterEntries) - // { - // PdfTextShaper.LayoutAndShapeText(pageSettings, Dictionaries, hf.Content); - // } - //} } //Collect Text Methods @@ -149,15 +184,14 @@ private PdfWorksheet[] GetPdfWorksheets(PdfPageSettings pageSettings, ExcelWorks return pdfSheets; } - private PdfWorksheet GetPdfWorksheet(PdfPageSettings pageSettings, ExcelWorksheet worksheet) + internal PdfWorksheet GetPdfWorksheet(PdfPageSettings pageSettings, ExcelWorksheet worksheet) { PdfWorksheet pdfSheet = new PdfWorksheet(); pdfSheet.Ranges = new List(); pdfSheet.Worksheet = worksheet; pdfSheet.Ranges = GetRanges(pdfSheet.Worksheet); - //pdfSheet.HeaderFooters = new PdfHeaderFooterCollection(pageSettings, Dictionaries, pdfSheet, pdfSheet.Worksheet.HeaderFooter); - if(pageSettings.ShowHeadings && AddTextForHeadings) Dictionaries.AddFont(pageSettings, pdfSheet.NormalStyle.Style.Font.Name, pdfSheet.GetSubFamilyFromNormalStyle, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); - AddTextForHeadings = false; + if (pageSettings.ShowHeadings && _addTextForHeadings) _dictionaries.AddFont(pageSettings, pdfSheet.NormalStyle.Style.Font.Name, pdfSheet.GetSubFamilyFromNormalStyle, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); + _addTextForHeadings = false; GetMaps(pageSettings, pdfSheet, pdfSheet.Ranges); GetHeaderFooter(pageSettings, pdfSheet); GetCommentsAndNotes(pageSettings, pdfSheet); @@ -170,8 +204,8 @@ private PdfWorksheet GetPdfWorksheet(PdfPageSettings pageSettings, ExcelRangeBas pdfSheet.Ranges = new List(); pdfSheet.Worksheet = excelRange.Worksheet; pdfSheet.Ranges.Add(new PdfRange(excelRange, false)); - if (pageSettings.ShowHeadings && AddTextForHeadings) Dictionaries.AddFont(pageSettings, pdfSheet.NormalStyle.Style.Font.Name, pdfSheet.GetSubFamilyFromNormalStyle, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); - AddTextForHeadings = false; + if (pageSettings.ShowHeadings && _addTextForHeadings) _dictionaries.AddFont(pageSettings, pdfSheet.NormalStyle.Style.Font.Name, pdfSheet.GetSubFamilyFromNormalStyle, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); + _addTextForHeadings = false; pdfSheet.Ranges[0] = GetMaps(pageSettings, pdfSheet, pdfSheet.Ranges[0]); GetHeaderFooter(pageSettings, pdfSheet); GetCommentsAndNotes(pageSettings, pdfSheet); @@ -210,14 +244,14 @@ private void GetMaps(PdfPageSettings pageSettings, PdfWorksheet pdfSheet, List + /// Returns the resolved color as a , taking theme, tint, indexed, RGB, and auto values into account. + /// Returns if the color is not set. + /// + public Color ToColor() + { + var hex = LookupColor(); + if (string.IsNullOrEmpty(hex) || hex == "0") return Color.Empty; + + // LookupColor returns strings like "#FFRRGGBB". Strip the leading '#'. + if (hex[0] == '#') hex = hex.Substring(1); + + int argb; + if (int.TryParse(hex, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out argb)) + { + return Color.FromArgb(argb); + } + return Color.Empty; + } } }