From 8591bd30763c160d77d6a35b30629ccbad372dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ossian=20Edstr=C3=B6m?= Date: Wed, 30 Sep 2026 12:41:05 +0200 Subject: [PATCH 1/2] AddPicture ImagePath if file not found --- src/EPPlus/Drawing/ExcelDrawings.cs | 33 ++++++++------ src/EPPlus/Drawing/ExcelPicture.cs | 20 +++++++-- src/EPPlusTest/Drawing/CopyDrawingTests.cs | 51 ++++++++++++++++++++-- src/EPPlusTest/Drawing/DrawingTest.cs | 4 +- 4 files changed, 87 insertions(+), 21 deletions(-) diff --git a/src/EPPlus/Drawing/ExcelDrawings.cs b/src/EPPlus/Drawing/ExcelDrawings.cs index 7cd6b77992..c0ead7a4c9 100644 --- a/src/EPPlus/Drawing/ExcelDrawings.cs +++ b/src/EPPlus/Drawing/ExcelDrawings.cs @@ -965,7 +965,14 @@ bool VerifyPath(string path) public ExcelPicture AddPicture(string Name, string ImagePath, PictureLocation Location = PictureLocation.Embed) { VerifyPath(ImagePath); - return AddPicture(Name, new FileInfo(ImagePath), null, Location); + + //var packageLocation = _package.File.FullName.Replace(_package.File.Name, ""); + //Uri imageUri = new Uri(new Uri(packageLocation), ImagePath); + //FileInfo verifyFile = null; + + //verifyFile = new FileInfo(imageUri); + var test = new FileInfo(ImagePath); + return AddPicture(Name, test, null, Location, null, ImagePath); } /// /// Adds a picture to the worksheet @@ -978,7 +985,7 @@ public ExcelPicture AddPicture(string Name, string ImagePath, PictureLocation Lo public ExcelPicture AddPicture(string Name, string ImagePath, ExcelHyperLink Hyperlink, PictureLocation Location = PictureLocation.Embed) { VerifyPath(ImagePath); - return AddPicture(Name, new FileInfo(ImagePath), Hyperlink, Location); + return AddPicture(Name, new FileInfo(ImagePath), Hyperlink, Location, null, ImagePath); } /// /// Adds a picture to the worksheet @@ -992,7 +999,7 @@ public ExcelPicture AddPicture(string Name, FileInfo ImageFile, PictureLocation return AddPicture(Name, ImageFile, null, Location); } - private ExcelPicture BaseAddPicture(string Name, FileInfo ImageFile, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed, object container = null) + private ExcelPicture BaseAddPicture(string Name, FileInfo ImageFile, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed, object container = null, string origImagePath = "") { XmlElement drawNode; switch (_collectionType) @@ -1005,22 +1012,22 @@ private ExcelPicture BaseAddPicture(string Name, FileInfo ImageFile, Uri Hyperli drawNode = CreateDrawingXml(eEditAs.OneCell); break; } - var type = PictureStore.GetPictureType(ImageFile.Extension); - - bool hasLink = (Location & PictureLocation.Link) == PictureLocation.Link; + var type = PictureStore.GetPictureType(ImageFile?.Extension); var pic = new ExcelPicture(this, drawNode, Hyperlink, type, Location, _collectionType); + bool hasLink = (Location & PictureLocation.Link) == PictureLocation.Link; + if (hasLink) { - pic.LoadImageLinked(ImageFile); + pic.LoadImageLinked(ImageFile, origImagePath); } return pic; } - internal ExcelPicture AddPicture(string Name, FileInfo ImageFile, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed, object container = null) + internal ExcelPicture AddPicture(string Name, FileInfo ImageFile, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed, object container = null, string origImagePath = "") { - var pic = BaseAddPicture(Name, ImageFile, Hyperlink, Location, container); + var pic = BaseAddPicture(Name, ImageFile, Hyperlink, Location, container, origImagePath); if (Location != PictureLocation.Link) { ValidatePictureFile(Name, ImageFile); @@ -1126,9 +1133,9 @@ public async Task AddPictureAsync(string Name, FileInfo ImageFile, /// Picture Hyperlink /// Location to access the image from /// A picture object - public async Task AddPictureAsync(string Name, FileInfo ImageFile, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed) + public async Task AddPictureAsync(string Name, FileInfo ImageFile, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed, string ImagePath = "") { - var pic = BaseAddPicture(Name, ImageFile, Hyperlink, Location); + var pic = BaseAddPicture(Name, ImageFile, Hyperlink, Location, null, ImagePath); if (Location != PictureLocation.Link) { ValidatePictureFile(Name, ImageFile); @@ -1152,7 +1159,7 @@ public async Task AddPictureAsync(string Name, FileInfo ImageFile, public async Task AddPictureAsync(string Name, string ImagePath, PictureLocation Location = PictureLocation.Embed) { VerifyPath(ImagePath); - return await AddPictureAsync(Name, new FileInfo(ImagePath), null, Location); + return await AddPictureAsync(Name, new FileInfo(ImagePath), null, Location, ImagePath); } /// /// Adds a picture to the worksheet @@ -1165,7 +1172,7 @@ public async Task AddPictureAsync(string Name, string ImagePath, P public async Task AddPictureAsync(string Name, string ImagePath, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed) { VerifyPath(ImagePath); - return await AddPictureAsync(Name, new FileInfo(ImagePath), Hyperlink); + return await AddPictureAsync(Name, new FileInfo(ImagePath), Hyperlink, Location, ImagePath); } /// /// Adds a picture to the worksheet from a stream. EPPlus will identify the type of image automatically. diff --git a/src/EPPlus/Drawing/ExcelPicture.cs b/src/EPPlus/Drawing/ExcelPicture.cs index bfa57a5124..563bf99aeb 100644 --- a/src/EPPlus/Drawing/ExcelPicture.cs +++ b/src/EPPlus/Drawing/ExcelPicture.cs @@ -22,6 +22,8 @@ Date Author Change using System.Globalization; using OfficeOpenXml.Utils.Image; using OfficeOpenXml.Utils.FileUtils; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; + #if NETFULL @@ -141,8 +143,16 @@ internal ExcelPicture(ExcelDrawings drawings, XmlNode node, ExcelGroupShape shap { container.RelPic = LinkedImageRel; Image = new ExcelImage(this); - FileInfo ImageFile = new FileInfo(LinkedImageRel.TargetUri.LocalPath); - LoadImageLinked(ImageFile); + + string adjustedPath = LinkedImageRel.TargetUri.IsAbsoluteUri ? LinkedImageRel.TargetUri.LocalPath : LinkedImageRel.TargetUri.OriginalString; + + //We need not "embed" as that is handled above but if we're a pure + //"link" we may want to load the linked image if it exists/the path is absolute + if(LinkedImageRel.TargetUri.IsAbsoluteUri && LinkedImageRel.TargetUri.IsFile) + { + FileInfo ImageFile = new FileInfo(adjustedPath); + LoadImageLinked(ImageFile); + } } } } @@ -232,7 +242,7 @@ internal void LoadImageWithoutSavingToPackage(Stream stream, ePictureType type) } } - internal void LoadImageLinked(FileInfo ImageFile) + internal void LoadImageLinked(FileInfo ImageFile, string origImagePath = "") { var uri = new Uri($"file:///{string.Format(ImageFile.FullName, CultureInfo.InvariantCulture)}"); var type = PictureStore.GetPictureType(ImageFile.Extension); @@ -240,6 +250,10 @@ internal void LoadImageLinked(FileInfo ImageFile) { LoadImageWithoutSavingToPackage(new FileStream(ImageFile.FullName, FileMode.Open, FileAccess.Read), type); } + else + { + uri = new Uri(string.Format(origImagePath, CultureInfo.InvariantCulture), UriKind.Relative); + } ContentType = PictureStore.GetContentType(type.ToString()); LinkedImageRel = _drawings.Part._rels.FirstOrDefault(x => x.TargetUri.OriginalString == uri.OriginalString); diff --git a/src/EPPlusTest/Drawing/CopyDrawingTests.cs b/src/EPPlusTest/Drawing/CopyDrawingTests.cs index 1ab47a2d51..52f124c926 100644 --- a/src/EPPlusTest/Drawing/CopyDrawingTests.cs +++ b/src/EPPlusTest/Drawing/CopyDrawingTests.cs @@ -1,13 +1,15 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using OfficeOpenXml; -using OfficeOpenXml.Table; +using OfficeOpenXml.Drawing; using OfficeOpenXml.Drawing.Chart; using OfficeOpenXml.Drawing.Chart.Style; +using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; +using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup.GroupingFunctions; +using OfficeOpenXml.Table; using System; using System.Collections.Generic; -using OfficeOpenXml.Drawing; -using System.Linq; using System.IO; +using System.Linq; namespace EPPlusTest.Drawing { @@ -951,5 +953,48 @@ public void GetFromAndToBounds_AbsoluteAnchor_ToColumn_ResolvesCorrectly() Assert.AreEqual(30, toColOff, "To column offset should be ~30 px."); } } + + [TestMethod] + public void s1078_EppGenerated() + { + //Ensure image does not exists when we first generate the package + var exists = GetOutputFile("images", "epplus_linkedLogo.png"); + if(File.Exists(exists.FullName)) + { + File.Delete(exists.FullName); + } + + using (var p = OpenPackage("s1078-gen-linked-picture-relative-target.xlsx", true)) + { + var ws = p.Workbook.Worksheets.Add("GeneratedLinkedPicWS"); + var linkedPic = ws.Drawings.AddPicture("myLinkedPic", "../images/epplus_linkedLogo.png", PictureLocation.Link); + + SaveAndCleanup(p); + } + + var file = GetOutputFile("images", "epplus_linkedLogo.png"); + var pngFile = GetResourceFile("Images/epplusobject.png"); + File.WriteAllBytes(file.FullName, File.ReadAllBytes(pngFile.FullName)); + + using (var p = OpenPackage("s1078-gen-linked-picture-relative-target.xlsx")) + { + var ws = p.Workbook.Worksheets[0]; + var genEpp = GetOutputFile("", "s1078-gen-resaved-linked-picture-relative-target.xlsx"); + p.SaveAs(genEpp); + } + } + + [TestMethod] + public void s1078_LinkedPicture() + { + using (var p = OpenTemplatePackage("s1078-linked-picture-relative-target.xlsx")) + { + var ws = p.Workbook.Worksheets[0]; + var strVal = ws.Cells["A1"].Value; + + SaveAndCleanup(p); + } + } + } } diff --git a/src/EPPlusTest/Drawing/DrawingTest.cs b/src/EPPlusTest/Drawing/DrawingTest.cs index 6d955de729..8807c3db74 100644 --- a/src/EPPlusTest/Drawing/DrawingTest.cs +++ b/src/EPPlusTest/Drawing/DrawingTest.cs @@ -913,7 +913,7 @@ public void ReadMultiChartSeries() pck.SaveAs(new FileInfo("c:\\temp\\chartseriesnew.xlsx")); } [TestMethod] - public void ChartWorksheet() + public void _0_ChartWorksheet() { var wsChart = _pck.Workbook.Worksheets.AddChart("ChartWorksheet", eChartType.Bubble3DEffect); var ws = _pck.Workbook.Worksheets.Add("data"); @@ -923,7 +923,7 @@ public void ChartWorksheet() wsChart.Chart.Series[0].Header = "Serie"; } [TestMethod] - public void ReadChartWorksheet() + public void _1_ReadChartWorksheet() { //Setup var wsName = "ChartWorksheet"; From cd133fa175ce4cc6dd97cc85cbbfc5e2ef83f545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ossian=20Edstr=C3=B6m?= Date: Thu, 1 Oct 2026 09:45:56 +0200 Subject: [PATCH 2/2] Removed adding unnecesary filepath to public async method --- src/EPPlus/Drawing/ExcelDrawings.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/EPPlus/Drawing/ExcelDrawings.cs b/src/EPPlus/Drawing/ExcelDrawings.cs index c0ead7a4c9..ce8cf547d2 100644 --- a/src/EPPlus/Drawing/ExcelDrawings.cs +++ b/src/EPPlus/Drawing/ExcelDrawings.cs @@ -1133,9 +1133,9 @@ public async Task AddPictureAsync(string Name, FileInfo ImageFile, /// Picture Hyperlink /// Location to access the image from /// A picture object - public async Task AddPictureAsync(string Name, FileInfo ImageFile, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed, string ImagePath = "") + public async Task AddPictureAsync(string Name, FileInfo ImageFile, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed) { - var pic = BaseAddPicture(Name, ImageFile, Hyperlink, Location, null, ImagePath); + var pic = BaseAddPicture(Name, ImageFile, Hyperlink, Location, null); if (Location != PictureLocation.Link) { ValidatePictureFile(Name, ImageFile); @@ -1174,6 +1174,20 @@ public async Task AddPictureAsync(string Name, string ImagePath, U VerifyPath(ImagePath); return await AddPictureAsync(Name, new FileInfo(ImagePath), Hyperlink, Location, ImagePath); } + + internal async Task AddPictureAsync(string Name, FileInfo ImageFile, Uri Hyperlink, PictureLocation Location = PictureLocation.Embed, string ImagePath = "") + { + var pic = BaseAddPicture(Name, ImageFile, Hyperlink, Location, null, ImagePath); + if (Location != PictureLocation.Link) + { + ValidatePictureFile(Name, ImageFile); + await pic.LoadImageAsync(new FileStream(ImageFile.FullName, FileMode.Open, FileAccess.Read), pic.Image.Type.Value); + } + AddPicture(Name, pic); + return pic; + } + + /// /// Adds a picture to the worksheet from a stream. EPPlus will identify the type of image automatically. ///