Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EndnotesPart.AddHyperlinkRelationship not generating proper endnotes.xml.rels #1778

Open
bhargavgaglani07 opened this issue Aug 26, 2024 · 4 comments
Assignees

Comments

@bhargavgaglani07
Copy link

bhargavgaglani07 commented Aug 26, 2024

Describe the bug
Adding an endnote having hyperlink into a document having no existing relationship for an endnote part is not generating proper endnotes.xml.rels file due to which generated document cannot be open using MS word or any other related application.

Screenshots

image
image

To Reproduce
Kindly run attached console app to reproduce the issue.
EndnotesWithURLIssue.zip

Observed behavior
If same code is executed (EndnotesPart.AddHyperlinkRelationship) for document already having an existing relationship present for endnotepart then generated document can be opened.

Expected behavior
EndnotesPart.AddHyperlinkRelationship should create a proper endnotes.xml.rels if not present.

Desktop (please complete the following information):

  • OS: windows 10
  • Office version: 16.0.17628.20188
  • .NET Target: 4.8
  • DocumentFormat.OpenXml Version: 2.5.0, 3.1.0

Debug Info
Adding below code before adding hyperlink relationship fixes the issue.

if(!document.Package.PartExists(new Uri("/word/_rels/endnotes.xml.rels", UriKind.Relative)))
{
    var part = document.Package.CreatePart(new Uri("/word/_rels/endnotes.xml.rels", UriKind.Relative), "application/vnd.openxmlformats-package.relationships+xml");
    part.Package.Flush();
}

@mikeebowen
Copy link
Collaborator

Hi @bhargavgaglani07,

I believe you see this issue because if there are no <endnotes/> element in the endnotes part if there are no existing endnotes. So, you need to add an <endnotes/> element before adding an endnote. I modified your code to do this and using the code below it will add the endnote and the relationship correctly. Also, FYI you don't need to call .Save() in a using statement. The document will save and dispose when the using closes.

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using W = DocumentFormat.OpenXml.Wordprocessing;

string dir = @"C:\source\tmp\";

if (!Directory.Exists(dir))
{
    Directory.CreateDirectory(dir);
}

string docxPath = @$"{dir}bbb - Copy.docx";
var outputPath = $"{dir}{DateTime.Now.Ticks}.docx";

Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"Processing file {Path.GetFileName(docxPath)}");

using (var memoryStream = File.Open(docxPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
using (var destinationStream = File.Create(outputPath))
using (var document = WordprocessingDocument.Open(memoryStream, true))
{



    Console.WriteLine($"Adding endnote having url");
    MainDocumentPart mainDocumentPart = document.MainDocumentPart ?? document.AddMainDocumentPart();
    EndnotesPart endNotesPart = mainDocumentPart.EndnotesPart ?? mainDocumentPart.AddNewPart<EndnotesPart>();

    if (endNotesPart.Endnotes is null)
    {
        endNotesPart.Endnotes = new Endnotes();
    }

    long endNoteId = GenerateEndnoteWithHyperlink(endNotesPart);

    mainDocumentPart.Document?.Body?.ChildElements.OfType<Paragraph>().LastOrDefault()?.InsertAfterSelf(GenerateParagraph(endNoteId));

    memoryStream.Seek(0, SeekOrigin.Begin);
    memoryStream.CopyTo(destinationStream);
}


try
{
    using (var document = WordprocessingDocument.Open(outputPath, true))
    {
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine($"Could open generated document ({outputPath})");
        Console.ForegroundColor = ConsoleColor.Yellow;
    }
}
catch (Exception ex)
{
    Console.ForegroundColor = ConsoleColor.Red;
    Console.WriteLine($"Could not open generated document ({outputPath}) due to error {ex.Message}");
    Console.ForegroundColor = ConsoleColor.Yellow;
}

Console.WriteLine();

Console.WriteLine("Hit enter to exit");
Console.ReadLine();

Paragraph GenerateParagraph(long endnoteId)
{
    Paragraph paragraph1 = new Paragraph();

    ParagraphProperties paragraphProperties1 = new ParagraphProperties();

    ParagraphMarkRunProperties paragraphMarkRunProperties1 = new ParagraphMarkRunProperties();
    Languages languages1 = new Languages() { Val = "en-IN" };

    paragraphMarkRunProperties1.Append(languages1);

    paragraphProperties1.Append(paragraphMarkRunProperties1);

    W.Run run1 = new W.Run();

    W.RunProperties runProperties1 = new W.RunProperties();
    Languages languages2 = new Languages() { Val = "en-IN" };

    runProperties1.Append(languages2);
    W.Text text1 = new W.Text();
    text1.Text = "Adding paragraph having endnote containing hyperlink.";

    run1.Append(runProperties1);
    run1.Append(text1);

    W.Run run2 = new W.Run();

    W.RunProperties runProperties2 = new W.RunProperties();
    RunStyle runStyle1 = new RunStyle() { Val = "EndnoteReference" };
    Languages languages3 = new Languages() { Val = "en-IN" };

    runProperties2.Append(runStyle1);
    runProperties2.Append(languages3);
    EndnoteReference endnoteReference1 = new EndnoteReference() { Id = endnoteId };

    run2.Append(runProperties2);
    run2.Append(endnoteReference1);

    paragraph1.Append(paragraphProperties1);
    paragraph1.Append(run1);
    paragraph1.Append(run2);
    return paragraph1;
}

long GenerateEndnoteWithHyperlink(EndnotesPart endnotesPart)
{
    long nextId = endnotesPart.Endnotes.ChildElements.OfType<Endnote>().Max(x => x.Id?.Value) + 1 ?? 1;

    Endnote endnote1 = new Endnote() { Id = nextId };

    Paragraph paragraph1 = new Paragraph();

    ParagraphProperties paragraphProperties1 = new ParagraphProperties();
    ParagraphStyleId paragraphStyleId1 = new ParagraphStyleId() { Val = "EndnoteText" };

    ParagraphMarkRunProperties paragraphMarkRunProperties1 = new ParagraphMarkRunProperties();
    Languages languages1 = new Languages() { Val = "en-IN" };

    paragraphMarkRunProperties1.Append(languages1);

    paragraphProperties1.Append(paragraphStyleId1);
    paragraphProperties1.Append(paragraphMarkRunProperties1);

    W.Run run1 = new W.Run();

    W.RunProperties runProperties1 = new W.RunProperties();
    RunStyle runStyle1 = new RunStyle() { Val = "EndnoteReference" };

    runProperties1.Append(runStyle1);
    EndnoteReferenceMark endnoteReferenceMark1 = new EndnoteReferenceMark();

    run1.Append(runProperties1);
    run1.Append(endnoteReferenceMark1);

    W.Run run2 = new W.Run();
    W.Text text1 = new W.Text() { Space = SpaceProcessingModeValues.Preserve };
    text1.Text = " ";

    run2.Append(text1);

    var relId = endnotesPart.AddHyperlinkRelationship(new Uri("https://www.dummyurlfromcode.com"), true).Id;

    W.Hyperlink hyperlink1 = new W.Hyperlink() { History = true, Id = relId };

    W.Run run3 = new W.Run() { RsidRunProperties = "005B5038" };

    W.RunProperties runProperties2 = new W.RunProperties();
    RunStyle runStyle2 = new RunStyle() { Val = "Hyperlink" };
    Languages languages2 = new Languages() { Val = "en-IN" };

    runProperties2.Append(runStyle2);
    runProperties2.Append(languages2);
    W.Text text2 = new W.Text();
    text2.Text = "https://www.dummyurlfromcode.com";

    run3.Append(runProperties2);
    run3.Append(text2);

    hyperlink1.Append(run3);

    paragraph1.Append(paragraphProperties1);
    paragraph1.Append(run1);
    paragraph1.Append(run2);
    paragraph1.Append(hyperlink1);

    endnote1.Append(paragraph1);
    endnotesPart.Endnotes.Append(endnote1);
    return endnote1.Id?.Value ?? throw new InvalidOperationException("Failed to generate a valid endnote id");
}

@bhargavgaglani07
Copy link
Author

bhargavgaglani07 commented Oct 14, 2024

Hi @mikeebowen,

Thank you for your response, but I would like to draw your attention to part where endnotes is already available in document and it also has 3 endnote. Check below for details.

Image

Here issue is that relationship part for XML is not getting generated by calling endnotesPart.AddHyperlinkRelationship(new Uri("https://www.dummyurlfromcode.com", UriKind.Absolute), true).

Endnote will get added but while opening the document it will fail as related hyperlink part is missing.

Refer below screenshot of running shared console application.

Image

Let me know if I am missing something or in case you need more information. Also note that, I tried adding below shared code but it will not get executed as document already have endnotes node along with multiple endnote.

if (endNotesPart.Endnotes is null)
{
    endNotesPart.Endnotes = new Endnotes();
}

Thanks,
Bhargav

@mikeebowen
Copy link
Collaborator

@bhargavgaglani07, Can you share your code? It works for me to add an endnote to an existing endnotes. The null check is there in case the document does not already have endnotes.

@bhargavgaglani07
Copy link
Author

Hi @mikeebowen,

Sure, please find attached console application, running the same will reproduce issue. Kindly note that here issue is not with adding endnote (no null reference exception is observed). Endnote gets added but when document is being opened at that time issue is observed due to missing reference part.

EndnotesWithURLIssue.zip

Let me know if you need more information.

Thanks
Bhargav

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants
@mikeebowen @bhargavgaglani07 and others