Issue with Page Numbers in Generated Document Using Aspose

Hello Team,

I have uploaded a document named Base_Document. I will be feeding some live data into the model, and during generation, this real-time data will be replaced using Aspose syntax.

However, after generation, the page numbers appear to be incorrect. I have also uploaded the Generated_Document for your reference.

Could you please help identify the possible reason for this issue and suggest how it can be resolved?

Below is the code I am using to build the generated document:

private static void UpdateReportData(Document doc, Study studyData) 
{
    R.ReportingEngine engine = new R.ReportingEngine();
    ReportingEngine.UseReflectionOptimization = false; 
    engine.BuildReport(doc, studyData, "Study"); 
}

Note: This issue was noticed only for this specific document not in others

Thank you.

Base_Document.docx (50.5 KB)

Generated_Document.docx (46.9 KB)

@KeerthanaRamesh19

Hello,

Thank you for reaching out to us regarding the issue with page numbers in your generated document using Aspose.

To help you better understand and resolve the issue, I will walk you through the common potential reasons and solutions for incorrect page numbers in Aspose.Words.

Common Reasons for Page Number Issues:

  1. Section Breaks:

    • Ensure that there are no unnecessary section breaks in your document that can cause the page numbering to reset.
  2. Page Numbering Settings:

    • Check the page numbering settings in the document to ensure they are correctly set.
  3. Document Structure:

    • Verify that the document structure (headers, footers, etc.) is preserved correctly during the generation process.
  4. Margins and Page Size:

    • Ensure that the margins and page size settings are consistent across all sections of the document.

Steps to Diagnosis and Fix the Issue:

  1. Inspect the Base Document:

    • Open the Base_Document using Aspose.Words and inspect the sections, page numbering settings, and document structure.
  2. Generate Document and Check:

    • Use the provided code to generate the document and compare the resulting Generated_Document with the base document.
  3. Correct Section Breaks:

    • If section breaks are not correct, adjust them using Aspose.Words APIs.
  4. Modify Page Numbering Settings:

    • Use Aspose.Words APIs to modify page numbering settings.

ASP.NET Example to Check and Update Page Numbers:

Here’s an example code snippet to check and update page numbers in a document using Aspose.Words:

using Aspose.Words;
using Aspose.Words.Markup;
using Aspose.Words.Saving;

private static void AdjustPageNumbers(Document doc)
{
    // Iterate through all sections to adjust page numbering
    foreach (Section section in doc.Sections)
    {
        // Get the header footer and check for page numbers
        HeaderFooter footer = section.Footers[FooterType.HeaderFooterPrimary];
        if (footer != null && footer.IsHeaderFooterPreservePageNumber)
        {
            // You can check other properties of the section and footer
            // To modify page number or section settings if required
        }
    }
}

// Example of using Aspose.Words to update the report data and adjust page numbers
private static void UpdateReportDataAndAdjustPageNumbers(Document doc, Study studyData)
{
    R.ReportingEngine engine = new R.ReportingEngine();
    ReportingEngine.UseReflectionOptimization = false;

    engine.BuildReport(doc, studyData, "Study");

    // Adjust page numbers after generating the report
    AdjustPageNumbers(doc);
}

Please refer to the Aspose.Words Documentation for more detailed information on working with sections, headers, footers, and document properties.

Follow-Up Questions:

  1. Are there any specific parts of the document where the page numbering is incorrect?
  2. Could you please provide more details about the structure and settings of your base document?
  3. Are you experiencing this issue with all documents or only specific ones?

Your cooperation will help us resolve the issue more effectively.

Feel free to reach out if you have any further questions or need additional support.

@KeerthanaRamesh19 Unfortunately, I cannot reproduce the problem on my side. Do you perform any postprocessing of the document after building the report? I used the following code for testing:

string jsonData = "{ Study : { Code : \"Some Code\" } }";
JsonDataSource ds = new JsonDataSource(new MemoryStream(Encoding.UTF8.GetBytes(jsonData)));

Document doc = new Document(@"C:\Temp\in.docx");

ReportingEngine engine = new ReportingEngine();
engine.BuildReport(doc, ds, "Study");

doc.Save(@"C:\Temp\out.docx");

Here is the produced output:
out.docx (44.1 KB)

Yes @alexey.noskov I do use

ProcessSubSuperScripts(doc);

private static void ProcessSubSuperScripts(Document doc)
{
    foreach (Node node in doc.GetChildNodes(NodeType.Paragraph, true))
    {
        if (node is Paragraph para)
        {
            ProcessParagraph(para);
        }
    }
}

private static void ProcessParagraph(Paragraph para)
{
    List<Run> updatedRuns = new();

    foreach (Run run in para.Runs.Cast<Run>())
    {
        updatedRuns.AddRange(ProcessTextWithFormatting(run));
    }

    if (updatedRuns.Count == 0) return; 

    para.Runs.Clear(); 
    foreach (Run newRun in updatedRuns)
    {
        para.AppendChild(newRun);
    }
}
private static List<Run> ProcessTextWithFormatting(Run run)
{
    string input = run.Text;
    if (string.IsNullOrEmpty(input))
        return new List<Run> { run };

    MatchCollection matches;
    try
    {
        matches = Pattern.Matches(input);
    }
    catch (RegexMatchTimeoutException)
    {
        Console.WriteLine("Regex match timed out.");
        return new List<Run> { run }; 
    }

    if (matches.Count == 0) return new List<Run> { run };

    List<Run> newRuns = new();
    int lastIndex = 0;

    foreach (Match match in matches)
    {
        if (match.Index > lastIndex)
            newRuns.Add(CloneRun(run, input.Substring(lastIndex, match.Index - lastIndex)));

        Run formattedRun = CloneRun(run, "");
        string content = ExtractContent(match);

        if (match.Value.StartsWith('^'))
        {
            formattedRun.Text = FormatGreekSymbols(content);
            formattedRun.Font.Superscript = true;
        }
        else if (match.Value.StartsWith('_'))
        {
            formattedRun.Text = FormatGreekSymbols(content);
            formattedRun.Font.Subscript = true;
        }
        else if (match.Value.StartsWith('\\'))
        {
            formattedRun.Text = FormatGreekSymbols(content);
        }

        newRuns.Add(formattedRun);
        lastIndex = match.Index + match.Length;
    }

    if (lastIndex < input.Length)
        newRuns.Add(CloneRun(run, input.Substring(lastIndex)));

    return newRuns;
}

private static readonly TimeSpan RegexTimeout = TimeSpan.FromSeconds(10);

private static readonly Regex Pattern = new(
    @"\^\{(.*?)\}|\^(\d+)|_\{(.*?)\}|_(\d+)|\\([a-zA-Z]+)",
    RegexOptions.Compiled,
    RegexTimeout
);


private static string ExtractContent(Match match)
{
    if (match.Groups[1].Success) return match.Groups[1].Value;
    if (match.Groups[2].Success) return match.Groups[2].Value;
    if (match.Groups[3].Success) return match.Groups[3].Value;
    if (match.Groups[4].Success) return match.Groups[4].Value;
    if (match.Groups[5].Success) return match.Groups[5].Value;
    return match.Value;
}

private static string FormatGreekSymbols(string content)
{
    if (GreekSymbols.TryGetValue(content, out string symbol))
    {
        return symbol;
    }

    if (content == "circ")
    {
        return "°";
    }

    return content;
}


private static Run CloneRun(Run original, string text)
{
    Run newRun = (Run)original.Clone(true);
    newRun.Text = text;
    return newRun;
}

private static readonly Dictionary<string, string> GreekSymbols = new()
{
    { "alpha", "α" }, { "beta", "β" }, { "gamma", "γ" }, { "delta", "δ" },
    { "epsilon", "ε" }, { "zeta", "ζ" }, { "eta", "η" }, { "theta", "θ" },
    { "iota", "ι" }, { "kappa", "κ" }, { "lambda", "λ" }, { "mu", "μ" },
    { "nu", "ν" }, { "xi", "ξ" }, { "omicron", "ο" }, { "pi", "π" },
    { "rho", "ρ" }, { "sigma", "σ" }, { "tau", "τ" }, { "upsilon", "υ" },
    { "phi", "φ" }, { "chi", "χ" }, { "psi", "ψ" }, { "omega", "ω" }
};

This code is supposed to process the subscript, Maybe this would have some issue. Can you help me identify the issue and how can I fix it?

@KeerthanaRamesh19 Thank you for additional information. Yes, the problem is caused by your code, which ruins node structure and as a result the fields are broken. Here is nodes structure in the header before processing with your code:


And after processing:

As you can see Run nodes that represent field code and values are moved outside the field. This causes the problem you observe.

If your goal is to make ^text superscripted and _text subscripted, you can easily achieve this using find/replace functionality of Aspose.Words. For example see the following code:

Dictionary<string, string> GreekSymbols = new Dictionary<string, string>()
{
    { "alpha", "α" }, { "beta", "β" }, { "gamma", "γ" }, { "delta", "δ" },
    { "epsilon", "ε" }, { "zeta", "ζ" }, { "eta", "η" }, { "theta", "θ" },
    { "iota", "ι" }, { "kappa", "κ" }, { "lambda", "λ" }, { "mu", "μ" },
    { "nu", "ν" }, { "xi", "ξ" }, { "omicron", "ο" }, { "pi", "π" },
    { "rho", "ρ" }, { "sigma", "σ" }, { "tau", "τ" }, { "upsilon", "υ" },
    { "phi", "φ" }, { "chi", "χ" }, { "psi", "ψ" }, { "omega", "ω" }
};

Document doc = new Document(@"C:\Temp\in.docx");

// Make numbers super or sub scripted.
FindReplaceOptions optSuperscript = new FindReplaceOptions();
optSuperscript.UseSubstitutions = true;
optSuperscript.ApplyFont.Superscript = true;
doc.Range.Replace(new Regex(@"\^(\d+)"), "$1", optSuperscript);

FindReplaceOptions optSubscript = new FindReplaceOptions();
optSubscript.UseSubstitutions = true;
optSubscript.ApplyFont.Subscript = true;
doc.Range.Replace(new Regex(@"_(\d+)"), "$1", optSubscript);

// Replace greek
foreach (string greek in GreekSymbols.Keys)
{
    doc.Range.Replace("^" + greek, GreekSymbols[greek], optSuperscript);
    doc.Range.Replace("_" + greek, GreekSymbols[greek], optSubscript);
}

doc.Save(@"C:\Temp\out.docx");
1 Like

And for
private static readonly Dictionary<string, string> GreekSymbols = new()
{
{ “alpha”, “α” }, { “beta”, “β” }, { “gamma”, “γ” }, { “delta”, “δ” },
{ “epsilon”, “ε” }, { “zeta”, “ζ” }, { “eta”, “η” }, { “theta”, “θ” },
{ “iota”, “ι” }, { “kappa”, “κ” }, { “lambda”, “λ” }, { “mu”, “μ” },
{ “nu”, “ν” }, { “xi”, “ξ” }, { “omicron”, “ο” }, { “pi”, “π” },
{ “rho”, “ρ” }, { “sigma”, “σ” }, { “tau”, “τ” }, { “upsilon”, “υ” },
{ “phi”, “φ” }, { “chi”, “χ” }, { “psi”, “ψ” }, { “omega”, “ω” }
};

for these above mentioned I will be passing as /alpha as in UI I want to populate it as α so how to replace that and its not superscript or sub script

@KeerthanaRamesh19 Just do not specify FindReplaceOptions.ApplyFont, i.e.

optSubscript.ApplyFont.Subscript = true;
1 Like

a^{67}
If this is provided then a⁶⁷
\alpha ^{2} _{beta} \omicron \circ

this means then α 2β ο °

how to achieve this way

for all of this case my code is working can we somehow achieve all this scenario

@KeerthanaRamesh19 Could you please provide input and expected output in form of DOCX document. Not sure forum shows the formatting correctly.

Sure, @alexey.noskov. I’ve uploaded the document that outlines all my expected behavior.
Also, I’m using KaTeX for the UI, which is why I’m using this kind of syntax.
Input_Expected_Output.docx (52.1 KB)

@KeerthanaRamesh19 Here is the modified code:

Document doc = new Document(@"C:\Temp\in.docx");

// Construct regular expression to replace Greek characters.
string regexString = @"\\?(";
foreach (string key in GreekCharacterReplacingCallback.GreekSymbols.Keys)
{
    regexString += key + "|";
}
regexString = regexString.TrimEnd('|') + ")";
Console.WriteLine(regexString);

// Replace Greek character keys with actual Greek characters.
FindReplaceOptions greekOptions = new FindReplaceOptions();
greekOptions.ReplacingCallback = new GreekCharacterReplacingCallback();
doc.Range.Replace(new Regex(regexString), "", greekOptions);

// Make values super or sub scripted.
FindReplaceOptions optSuperscript = new FindReplaceOptions();
optSuperscript.UseSubstitutions = true;
optSuperscript.ApplyFont.Superscript = true;
doc.Range.Replace(new Regex(@"\^([\d\w])"), "$1", optSuperscript);
doc.Range.Replace(new Regex(@"\^\{([\d\w]+)\}"), "$1", optSuperscript);

FindReplaceOptions optSubscript = new FindReplaceOptions();
optSubscript.UseSubstitutions = true;
optSubscript.ApplyFont.Subscript = true;
doc.Range.Replace(new Regex(@"_([\d\w])"), "$1", optSubscript);
doc.Range.Replace(new Regex(@"_\{([\d\w]+)\}"), "$1", optSubscript);

doc.Save(@"C:\Temp\out.docx");
private class GreekCharacterReplacingCallback : IReplacingCallback
{
    public ReplaceAction Replacing(ReplacingArgs args)
    {
        if (GreekSymbols.ContainsKey(args.Match.Groups[1].Value))
        {
            args.Replacement = GreekSymbols[args.Match.Groups[1].Value];
            return ReplaceAction.Replace;
        }
        return ReplaceAction.Skip;
    }

    public readonly static Dictionary<string, string> GreekSymbols = new Dictionary<string, string>()
    {
        { "alpha", "α" }, { "beta", "β" }, { "gamma", "γ" }, { "delta", "δ" },
        { "epsilon", "ε" }, { "zeta", "ζ" }, { "eta", "η" }, { "theta", "θ" },
        { "iota", "ι" }, { "kappa", "κ" }, { "lambda", "λ" }, { "mu", "μ" },
        { "nu", "ν" }, { "xi", "ξ" }, { "omicron", "ο" }, { "pi", "π" },
        { "rho", "ρ" }, { "sigma", "σ" }, { "tau", "τ" }, { "upsilon", "υ" },
        { "phi", "φ" }, { "chi", "χ" }, { "psi", "ψ" }, { "omega", "ω" },
        { "circ", "°" } // Also added circ key here
    };
}

out.docx (45.1 KB)

1 Like

There was an issue If I have something like Details its getting converted to Dηils this kind of problem I don’t want to get…

@KeerthanaRamesh19 You can adjust regular expression to skip matching such cases. For example, you can add whitespace at the beginning and at the end of regular expression.

..................
// Construct regular expression to replace Greek characters.
string regexString = @" \\?(";
foreach (string key in GreekCharacterReplacingCallback.GreekSymbols.Keys)
{
    regexString += key + "|";
}
regexString = regexString.TrimEnd('|') + ") ";
..................

this will work as usual correct…? in all the above mentioned cases and also will there be any drawbacks of this…?

@KeerthanaRamesh19 The provided code demonstrates the basic technique, that you can use. You should test with your real document to make sure it covers all your cases properly and if not adjust the code according to your needs.

1 Like