From 40b8d1dae5c0712213ae452f03348c2f801cb608 Mon Sep 17 00:00:00 2001 From: Artjom Date: Tue, 25 Aug 2026 12:25:32 +0200 Subject: [PATCH] Accept root-relative internal relationship targets (Mono fix) An internal relationship whose Target begins with '/' is a root-relative path, which OPC permits. ParseRelationship rejected it because Uri.IsAbsoluteUri disagrees across runtimes for that shape: .NET parses "/xl/sharedStrings.xml" as a RELATIVE Uri, Mono parses it as ABSOLUTE. On Mono the relationship is therefore discarded as "an internal relationship target cannot be an absolute URI". Because sharedStrings is looked up as an optional relationship, the reader then skips the shared strings part silently and every string cell is returned holding its raw shared-string INDEX instead of its text -- no exception, correct cell count, wrong values. Microsoft Excel writes exactly this shape: in a workbook produced by Excel 16.0, 26 of 27 relationships are plain relative and the sharedStrings one is "/xl/sharedStrings.xml", so any Excel-authored workbook can hit it. Reproduced on Mono 6.12 (Unity's editor runtime); .NET 8/10 are unaffected. Test for a real scheme rather than trusting IsAbsoluteUri, and rebuild the target as relative so PackUriHelper.ResolvePartUri agrees on both runtimes. A genuinely absolute target (http://...) is still rejected. --- .../Internal/Readers/DiscoveryReader.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/NanoXLSX.Reader/Internal/Readers/DiscoveryReader.cs b/NanoXLSX.Reader/Internal/Readers/DiscoveryReader.cs index 7cbe62e73..8221afed6 100644 --- a/NanoXLSX.Reader/Internal/Readers/DiscoveryReader.cs +++ b/NanoXLSX.Reader/Internal/Readers/DiscoveryReader.cs @@ -1,4 +1,4 @@ -/* +/* * NanoXLSX is a small .NET library to generate and read XLSX (Microsoft Excel 2007 or newer) files in an easy and native way * Copyright Raphael Stoeckli © 2026 * This library is licensed under the MIT License. @@ -238,10 +238,23 @@ private RelationshipInfo ParseRelationship(XmlReader reader, string relationship string resolvedTargetPath = null; if (targetMode == TargetMode.Internal) { - if (targetUri.IsAbsoluteUri) + // [VENDOR-PATCH NanoXLSX#mono-rootrelative-target] A target beginning with '/' is a + // ROOT-RELATIVE path, which OPC permits for an internal relationship. Mono's System.Uri + // parses such a string as an ABSOLUTE Uri while .NET's does not, so testing IsAbsoluteUri + // alone silently discards every root-relative relationship on Mono. In practice that is + // sharedStrings.xml: the relationship is dropped from the catalog, the (optional) shared + // strings part is never read, and every string cell is left holding its raw shared-string + // INDEX instead of its text -- with no exception raised. Test for a real scheme instead, + // and rebuild the target as relative so ResolvePartUri agrees on both runtimes. + bool rootRelativeTarget = target.StartsWith("/", StringComparison.Ordinal); + if (targetUri.IsAbsoluteUri && !rootRelativeTarget) { return HandleRelationshipIssue(relationshipPartPath, id, "An internal relationship target cannot be an absolute URI.", issues, null); } + if (rootRelativeTarget) + { + targetUri = new Uri(target, UriKind.Relative); + } Uri sourceUri = new Uri("/" + sourcePartPath, UriKind.Relative); Uri resolvedTargetUri = PackUriHelper.ResolvePartUri(sourceUri, targetUri); resolvedTargetPath = resolvedTargetUri.OriginalString.TrimStart('/');