Repository navigation
Make the library fully cross-platform: remove GDI+ (System.Drawing.Common), CI on Linux - #6
Conversation
…p text measurement Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…; add header reader tests Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…d precision test bugs; release notes, NBGV 11.0 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| BestPractice | 1 high |
| ErrorProne | 1 high |
| CodeStyle | 1 minor |
| Complexity | 4 medium |
🟢 Metrics 54 complexity · -3 duplication
Metric Results Complexity 54 Duplication -3
🟢 Coverage 73.40% diff coverage · +9.61% coverage variation
Metric Results Coverage variation ✅ +9.61% coverage variation (-1.00%) Diff coverage ✅ 73.40% diff coverage Coverage variation details
Coverable lines Covered lines Coverage Common ancestor commit (85c2320) 47155 25865 54.85% Head commit (bce00dd) 47272 (+117) 30473 (+4608) 64.46% (+9.61%) Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch:
<coverage of head commit> - <coverage of common ancestor commit>Diff coverage details
Coverable lines Covered lines Diff coverage Pull request (#6) 406 298 73.40% Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified:
<covered lines added or modified>/<coverable lines added or modified> * 100%
AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Pull Request Overview
The Linux CI command now runs previously excluded tests, including documented failures and host-crashing culture tests, so the pipeline may fail for reasons unrelated to this change. The culture test also leaks state when assertions fail.
Image-header parsing remains a high-risk, uncovered area: TIFF/EMF/WMF scenarios and malformed-offset handling are not sufficiently tested. The autofit fallback also leaves DoAdjustDrawings unrestored on an early return.
About this PR
- Verify that active tests use platform-neutral temporary paths and that CI explicitly runs the complete unfiltered suite on ubuntu-latest.
Test suggestions
- Add, save, reopen, and read picture bytes through the public API on a non-Windows platform
- Validate PNG, JPEG, GIF, BMP, and truncated image headers
- Validate TIFF, EMF, and WMF dimensions, resolution, and content types
- Exercise Stream and FileInfo picture overloads and verify bytes remain unchanged
- Exercise background-image byte storage and replacement/removal
- Exercise header/footer and VML picture byte APIs
- Verify autofit with SkiaSharp and the unavailable-native-library fallback
- Verify autofit restores DoAdjustDrawings on every exit path
- Copy worksheets containing pictures without image decoding
- Add malformed-offset and unsupported-TIFF-field tests for ImageHeaderReader
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Validate TIFF, EMF, and WMF dimensions, resolution, and content types
2. Exercise background-image byte storage and replacement/removal
3. Exercise header/footer and VML picture byte APIs
4. Verify autofit with SkiaSharp and the unavailable-native-library fallback
5. Verify autofit restores DoAdjustDrawings on every exit path
6. Add malformed-offset and unsupported-TIFF-field tests for ImageHeaderReader
TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback
| private static (int, int, float, float) ReadTiff(ReadOnlySpan<byte> b) | ||
| { | ||
| var little = b[0] == 0x49; | ||
| ushort U16(ReadOnlySpan<byte> s, int o) => little ? BinaryPrimitives.ReadUInt16LittleEndian(s[o..]) : BinaryPrimitives.ReadUInt16BigEndian(s[o..]); | ||
| uint U32(ReadOnlySpan<byte> s, int o) => little ? BinaryPrimitives.ReadUInt32LittleEndian(s[o..]) : BinaryPrimitives.ReadUInt32BigEndian(s[o..]); | ||
| double Value(ReadOnlySpan<byte> s, int entry) | ||
| { | ||
| var type = U16(s, entry + 2); | ||
| switch (type) | ||
| { | ||
| case 3: | ||
| return U16(s, entry + 8); // SHORT | ||
| case 4: | ||
| return U32(s, entry + 8); // LONG | ||
| case 5: // RATIONAL | ||
| var at = (int)U32(s, entry + 8); | ||
| return (double)U32(s, at) / Math.Max(1U, U32(s, at + 4)); | ||
| default: | ||
| return 0; | ||
| } | ||
| } | ||
|
|
||
| var ifd = (int)U32(b, 4); | ||
| var count = U16(b, ifd); | ||
| double width = 0, height = 0, xres = 0, yres = 0; | ||
| var unit = 2; | ||
| for (var i = 0; i < count; i++) | ||
| { | ||
| var entry = ifd + 2 + (i * 12); | ||
| switch (U16(b, entry)) | ||
| { | ||
| case 256: | ||
| width = Value(b, entry); | ||
| break; | ||
| case 257: | ||
| height = Value(b, entry); | ||
| break; | ||
| case 282: | ||
| xres = Value(b, entry); | ||
| break; | ||
| case 283: | ||
| yres = Value(b, entry); | ||
| break; | ||
| case 296: | ||
| unit = (int)Value(b, entry); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| var scale = unit == 3 ? 2.54 : 1; | ||
| return ((int)width, (int)height, (float)(xres * scale), (float)(yres * scale)); |
There was a problem hiding this comment.
🔴 HIGH RISK
Add focused tests for TIFF, EMF, and WMF dimensions and DPI, truncated and malformed offsets, and unsupported TIFF field types. Add an explicit TIFF switch default that deliberately ignores optional fields or rejects unsupported required data with InvalidDataException.
| dotnet test PanoramicData.EPPlus.Test/PanoramicData.EPPlus.Test.csproj | ||
| --configuration Release --no-build | ||
| --collect "Code Coverage;Format=Cobertura" --results-directory ./coverage | ||
| --blame-hang-timeout 5m |
There was a problem hiding this comment.
🔴 HIGH RISK
Retain or replace exclusions for known failing tests, or fix those tests before removing the filter; otherwise this command re-enables documented failures in the Linux CI run.
| { | ||
| throw new Exception("Name already exists in the drawings collection"); | ||
| } | ||
| throw new Exception("AddPicture: ImageFile can't be null"); |
There was a problem hiding this comment.
| @@ -165,15 +175,15 @@ public ExcelVmlDrawingPicture InsertPicture(FileInfo PictureFile, PictureAlignme | |||
|
|
|||
| var contentType = ExcelPicture.GetContentType(PictureFile.Extension); | |||
| var uriPic = XmlHelper.GetNewUri(_ws._package.Package, "/xl/media/" + PictureFile.Name[..^PictureFile.Extension.Length] + "{0}" + PictureFile.Extension); | |||
| var imgBytes = ImageCompat.GetImageAsByteArray(Picture); | |||
|
|
|||
| var ii = _ws.Workbook._package.AddImage(imgBytes, uriPic, contentType); | |||
|
|
|||
| return AddImage(Picture, id, ii); | |||
| return AddImage(header, id, ii); | |||
There was a problem hiding this comment.
🟡 MEDIUM RISK
Add cross-platform tests for InsertPicture(byte[]), InsertPicture(Stream), and InsertPicture(FileInfo), including dimensions, invalid/truncated data, duplicate-section validation, and verification that stored package bytes remain unchanged.
| //No usable font engine (native SkiaSharp library missing): leave the widths as they are. | ||
| _worksheet.Drawings.AdjustWidth(drawWidths); | ||
| _worksheet._package.DoAdjustDrawings = doAdjust; | ||
| return; |
There was a problem hiding this comment.
🟡 MEDIUM RISK
Restore DoAdjustDrawings before returning from this path, as in the normal completion path.
| System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("sv-SE", false); | ||
| var sv = new CultureInfo("sv-SE", false); | ||
| Assert.AreEqual(new DateTime(2018, 12, 31).ToString("D", sv), ws.Cells["A1"].Text); | ||
| Assert.AreEqual(ws.GetValue<DateTime>(1, 1), new DateTime(2018, 12, 31)); | ||
| System.Threading.Thread.CurrentThread.CurrentCulture = pCulture; |
There was a problem hiding this comment.
🟡 MEDIUM RISK
Restore the previous culture in a finally block surrounding the assertions.
| Create(node, imageBytes, contentType, "/xl/media/image{0}" + GetExtension(contentType), widthPixels, heightPixels, ImageHeaderInfo.DefaultDpi, ImageHeaderInfo.DefaultDpi, hyperlink); | ||
| } | ||
|
|
||
| private void Create(XmlNode node, byte[] imageBytes, string contentType, string uriTemplate, int widthPixels, int heightPixels, float horizontalDpi, float verticalDpi, Uri hyperlink) |
There was a problem hiding this comment.
🟡 MEDIUM RISK
Suggestion: Consider introducing an image metadata/options object to replace the positional parameter list.
Summary
Removes every dependency on the GDI+ parts of System.Drawing so the library and its whole test suite run on Linux, and puts CI back on
ubuntu-latest.System.Drawing.Color/Point/Size(plain structs in the shared framework) are still used.System.Drawing.Commonpackage reference removed; allImage/Bitmap/Graphics/Font/Imaginguse and all[SupportedOSPlatform("windows")]/OperatingSystemchecks removed.ImageHeaderReaderreads width/height/DPI from PNG, JPEG, GIF, BMP, TIFF, EMF, WMF headers and sniffs the content type. File bytes are stored verbatim (no re-encoding to JPEG). Loading a picture from a package no longer throws off Windows.TextMeasureruses SkiaSharp (MIT) +SkiaSharp.NativeAssets.Linux. No ImageSharp. If the native library cannot load,AutoFitColumnsleaves widths unchanged (as the old GDI-missing path did) and now restoresDoAdjustDrawings.PackageReleaseNotes.Breaking API changes
Removed:
ExcelDrawings.AddPicture(string, Image)and(string, Image, Uri)ExcelPicture.Image,ExcelPicture.ImageFormatExcelBackgroundImage.ImageExcelVmlDrawingPicture.ImageExcelHeaderFooterText.InsertPicture(Image, PictureAlignment)ExcelFont.SetFromFont(Font),ExcelTextFont.SetFromFont(Font),ExcelFontXml.SetFromFont(Font)Added / changed:
ExcelDrawings.AddPicture(string, byte[]),(string, byte[], Uri),(string, Stream),(string, Stream, Uri); theFileInfooverloads remain (read bytes)ExcelPicture.ImageBytes(get/set),ExcelPicture.ContentTypenow publicExcelBackgroundImage.ImageBytes,SetFromStream;SetFromFilekeptExcelVmlDrawingPicture.ImageBytesExcelHeaderFooterText.InsertPicture(byte[], ...),InsertPicture(Stream, ...);FileInfooverload keptTests
SetFromFont(new Font(..))call sites to property setters (DrawingTest, WorkSheetTests, SampleApp);Resources.Test1is nowbyte[];SetBackgroundusesImageBytes; Issue100/Issue10 (ignored) use bytes.AddPictureFromBytes_RoundTripsThroughASavedPackage: platform branch removed; now reopens and reads the picture back viaImageBytes.AddPicture(byte[])auto-size, Stream and FileInfo overloads.EPPlusExcelCalculationSample(WinForms, not in the solution) is untouched.DateFunctionsWorkWithDifferentCultureDateFormats_*: exception on the worker thread crashed the host; now captured and asserted on the test thread, and the culture is created without user overrides.RunDrawingTests/SetBackground:TestBaseextracted clipart using the wrong resource-name prefix; fixed. Also path separators (\) inTestBaseand several tests replaced withPath.Combine.RunWorksheetTests: assertions compared against 15-digit literals; use the full round-trip values.Issuer246: assertion arguments/expected text depended on OS culture data; now computed from the sv-SE long date.Issue333: sets sv-SE explicitly (the format is the culture short date).TestAssemblySetupfixes the suite to en-US.--filterexclusions removed from CI (--blame-hang-timeout 5mand the Codacy upload are kept). Locally on Windows the entire suite passes with no filter: 1176 passed, 214 skipped (pre-existing[Ignore]s).CI
buildjob isubuntu-latest; no Windows runner anywhere.🤖 Generated with Claude Code