Expressions in template generate empty lines

I am new to Aspose, and have been working through the documentation. We are attempting to create a template using Word, and Aspose expressions, so that a user can modify a word file using Aspose’s Expression syntax, then generate a document using that template.

One issue I am running into is that anywhere there is an expression line, Aspose inserts a carriage return, even if that expression is false, or is a control expression.

For example:
<<foreach [sample in Samples]>>
<<[sample.SampleText]:“format” -html >>
<>

The above set of code will generate n+2 lines (line line for the first foreach, which will be blank, one line for each sample, and one blank line for the end for each.

Another example:
<<[MyTable.First().Name]>>

If the row in the example above is null or empty, it still returns a carriage return in that section.

Is there any way to not have a carriage return if the resulting expression has no value? Likewise, is there a way to not have carriage returns on lines where there is an expression?

Otherwise, I end up with a whole bunch of carriage returns throughout the document.

Sorry if this is in the documentation and I have missed it.
Any help would be appreciated.

Thanks!

@jepthy,

Thanks for your inquiry. Please modify your template document as below. Hope this helps you.

<<foreach [sample in Samples]>><<[sample.SampleText]:“format” -html >><</foreach>>

<<if [MyTable.First().Name == null]>>
<<[MyTable.First().Name]>><</if>>

If you still face problem, please attach the following resources here for testing:

  • Your input Word document.
  • Please attach the output Word file that shows the undesired behavior.
  • Please attach the expected output Word file that shows the desired behavior.
  • Please create a standalone console application (source code without compilation errors) that helps us to reproduce your problem on our end and attach it here for testing.

As soon as you get these pieces of information ready, we will start investigation into your issue and provide you more information. Thanks for your cooperation.

PS: To attach these resources, please zip and upload them.

Thank you Tahir. I will work on getting you a sample program to reproduce the error.
I have also come across a few other issues, which I will include in my samples. Namely, how to reset continuous numbering, etc.

I believe a majority of my problem is the location of the paragraph tags within the template itself. If there is a paragraph tag in the word template, it gets rendered outside of the template in the resulting document, even if the only thing on that line wasn’t rendered.

It should make more sense when I get the code uploaded. Give me a couple of hours to get something together. Thank you so much for the response!

I have added a Visual Studio 2017 project that shows the issue I am having.

In the Templates folder (Aspose Samples\AsposeTemplateGeneration\AsposeTemplateGeneration\Templates), I have included a word document of the template, expected output, and the actual output.
In the Actual output file, I included comments on the oddities that I am seeing. Any chance with pointers on how to fix these issues?

The first issue is that with the Expressions used in the template, I have to be very, very, very careful where carriage returns are located. It makes the template almost unreadable and unsupportable.

For example, instead of:
<<foreach [sample in Samples]>>
<<[sample.SampleText]>>
<>

I have to do it all on one line like this:
<<foreach [sample in Samples]>><<[sample.SampleText]>><>

For one or two items, ok, I can see that. But we ended up having more complicated logic with nested if statements, etc. Makes it hard to visualize and edit if it is all on one line. Is there a way for us to remove the empty paragraphs afterwards, ignore lines that just have markup, etc? I believe you have something like that with the Mail Merge functionality. How about the Linq Reporting?

The second issues is where we inject HTML blocks, and it is picking up the begining and closing paragraph tags and adding new items to an ordered list from the HTML.

The third issue is we can’t seem to find a way in the template language to tell an Ordered List to start over at 1. If I have two lists separated by a paragraph tags, the second list continues the numbering started in the first list.

The fourth issue is that when HTML is injected into an Ordered list, if there are new lines in the HTML they end up being inserted as new Ordered items, rather than just under the main block.

Any pointers you could provide would be greatly appreciated.
Thank you!

Aspose Samples.zip (425.1 KB)

@jepthy,

Thanks for your inquiry. We are working over it and will get back to you soon.

@jepthy,

Please use the attached modified template document to get the desired output. Template.zip (11.4 KB)

We have logged this feature as WORDSNET-17235 in our issue tracking system to remove empty list item at the end of group.

Unfortunately, Aspose.Words does not support this feature. We have logged this feature as WORDSNET-17234 in our issue tracking system.

You are inserting HTML into document that contains the list items and paragraphs. This is expected behavior of Aspose.Words.

Thank you very much.
If I understand what you are saying correctly:

  1. we need to be very careful where the Paragraph tags are within the Template Markup code, as it will make its way into the resulting document. I.E. if we have a line that only has markup, it will end up as a blank line on the resulting document.
  2. There currently isn’t a way to tell the system to restart numbering on a numbered list. This will be marked as a possible feature request.
  3. If there are multiple paragraph tags in an HTML segment being inserted into an ordered list, it ends up creating multiple line items. This is by design, and there is no way to get around this behavior.

Thank you for your response.

One last item. Do you know if it is possible to merge both template language, and custom code. I am thinking about writing my own code that will do what I need.
For example, in the instance where I want to insert an ordered list, and have it restart numbering, could I write my own method and have it insert an ordered list?
It would look something like this:
<<[F.InsertOrderedList(samples)]>>

I would then write my own C# code as a static method in an F class, which would take a collection of samples, and return an ordered list.

@jepthy,

Thanks for your inquiry. Yes, your understanding is correct.

We logged a feature request to remove the last empty item number from the list using LINQ Reporting engine.

Yes, it is possible. You can generate separate document for each Group and merge them into one single output. Please refer to the following article about joining documents.
Joining and Appending Documents

Tahir,
Thank you.

I am working on creating an Extension method that will create a blank document, add the data I need, and then inject it into the template.

Here is my code, as an example:
public static class ListHelper{
public static Document InsertHelloWorld()
{
var document = new Document();
DocumentBuilder builder = new DocumentBuilder(document);
builder.Write(“Hello World”);
return document;
}
}

I am registering my class with Aspose, and from within my template, I make a call to this method using:
<<doc [ListHelper.InsertHelloWorld()]>>

It feels like I might be on the right path. Using this paradigm, I should be able to create uniquely ordered lists by returning them in a sub-document, and appending that to the document.

The biggest issue I am running into is in the formatting of the resulting document.

For instance, if I have indented <<doc [ListHelper.InsertHelloWorld()]>> three times, when it actually renders the “Hello World” text in the resulting document, it is no longer indented.

The examples I can see to keep source formatting don’t seem to apply here.

Am I missing something? Is this possible?

Along those same lines, is it possible to build an extension method that can return just a paragraph object, and have it inserted? I.E. something like this…

<<[ListHelper.CreateParagraph()]>>

public static void DocumentBuilder CreateParagraph(){
var results = new DocumentBuilder();
results.Write(“Hello World”);
return results;
}

Or is there a way to pass the paragraph you are currently on, into an Extension method?
I.E.
<<[ListHelper.CreateList(this)]>>
public static Paragraph CreateList(Document doc){
var builder = new DocumentBuilder(doc);
builder.Write(“Hello World”);
return builder.CurrentParagraph;
}

@jepthy,

Thanks for your inquiry. You can insert the sub-document using LINQ Reporting. Please read following article.
Inserting Documents Dynamically

The better way to list the numbers for each group is to use NumberOf extension method. Please refer to the following article. Hope this helps you.
Using Extension Methods of Iteration Variables

Tahir,
We are using the doc tag to insert the sub document. The issue we are running into is that if I indent the <> tag three times in the master document, when the sub-document is inserted it looses those indents.

Attached is a sample project.

I am trying to work around some of the limitations we have found with the Linq template language. Namely, if there are

tags in HTML, and you insert them into a list, then it will create new list items. And trying to re-order a numbered list.

The use case that we have is a user is able to enter data into a website using a rich text box.
This information will eventually make its way into a report, generated using Aspose.

In the report, we want to number the items they have added.
So think of a candy manufacturing plant, where they sample pieces of candy coming off the line, and making comments on a website for quality control.

The user would go to the website and list out the following:
Candy: Lolipop
Sample 1: Tasted really good

Sample 2: Color was bright and vibrant

Sample 3:
John added to much sugar.
Not sure why John added that much sugar, need to do the following three things:

  1. Check the amount of sugar there should have been
  2. Check the amount John added
  3. Write up John

When we go to actually generate the document, we want to show what was entered above like this:

Sample Type:
Lolipop (3 Samples)
Samples:

  1. Tasted really good
  2. Color was bright and vibrant
  3. John added to much sugar.
    Not sure why John added that much sugar, need to do the following three things:
    1. Check the amount of sugar there should have been
    2. Check the amount John added
    3. Write up John

But with the Template language, it comes out like this:

Sample Type:
Lolipop (3 Samples)
Samples:

  1. Tasted really good
  2. Color was bright and vibrant
  3. John added to much sugar.
  4. Not sure why John added that much sugar, need to do the following three things:
    a. Check the amount of sugar there should have been
    b. Check the amount John added
    c. Write up John

You will notice that we told the user there were 3 samples, but there are 4 line items because when we inserted HTML, it saw the

tag on the third sample and created a new item.
It actually gets a little trickier because with the Rich Text control we are using (quill), it adds a

tag to every line, so we end up with like 8 different lines. We can strip some of the characters, but in some cases having the paragraph tag is valid.

We also run into the problem mentioned before that when we start on the next list, it doesn’t start numbering at 1, it keeps the numbering going.

So I thought I could work on an extension method to pass in the arguments I want on the list, have it create the list in a blank document, then insert it where needed.

So far, this is actually going pretty well. I can have it restart the numbering on the list, and I have found a way to indent the HTML so it doesn’t create a new line item.

However, I loose things like tabs in the source document.
I.E. if my tag looks like this, with three tabs to the left:
<<doc [ListHelper.InsertList()]>>
When I look at the generated document, that list is no longer indented three times.

Also, it would be cool if I could pass into that extension method the Document it is being inserted into, so that I can work with the document itself, rather than appending a new one.

Hope this makes sense. You should be able to see what I am attempting in the attached sample.

Also, you may be asking why we are using a numbered list rather than a table, or some other method where we can use NumberOf to write out the sample number. The user may be editing this document after it is generated, and it is easier for them to continue a numbered list, as opposed to inserting a table row within word.

Aspose Samples.zip (379.5 KB)

@jepthy,

Thanks for your inquiry. We are working over your query and will get back to you soon.

@jepthy,

Thanks for your patience. In your case, you are facing following three issues.

  1. Restart the list number for each group.
  2. Remove the last empty list item.
  3. Remove empty paragraphs

You are facing the expected behavior of Aspose.Words in all your shared scenarios. Please note that Aspose.Words mimics the behavior of MS Word. In your case, we suggest you please modify the generated document by LINQ Reporting to get the desired output. Please use Node.Remove method to remove the empty paragraph. You can use Node.ToString(SaveFormat.Text) method to check either paragraph has text or not.

Following code example shows how to remove the last empty list item and restart the list. Hope this helps you.

Document doc = new Document(@"c:\temp\Template.docx");
ReportingEngine engine = new ReportingEngine();
List<string> names = new List<string> { "SampleGroups", "Samples" };
List<object> values = new List<object> { Data.GetSampleGroupsDataTable(), Data.GetSamplesDataTable() };
             
engine.BuildReport(doc, dataSources: values.ToArray(), dataSourceNames: names.ToArray());


foreach (Paragraph para in doc.GetChildNodes(NodeType.Paragraph, true))
{
    if (para.IsListItem && para.ToString(SaveFormat.Text).Trim().Length == 0)
    {
        para.Remove();
    }
}

IEnumerable<Paragraph> paragraphList = doc.GetChildNodes(NodeType.Paragraph, true).Cast<Paragraph>().ToList<Paragraph>().Where(p => p.IsListItem == true);

Aspose.Words.Lists.List dstList = null;
foreach (var item in paragraphList)
{
    dstList = doc.Lists.AddCopy(item.ListFormat.List);
    break;
}

Boolean blnListStart = false;
foreach (Paragraph para in doc.GetChildNodes(NodeType.Paragraph, true))
{
    if (para.IsListItem && para.ListFormat.ListLevelNumber == 0)
    {
        para.ListFormat.List = dstList;
        blnListStart = true;
    }
    else
    {
        if (blnListStart)
        {
            dstList = doc.Lists.AddCopy(dstList);
            blnListStart = false;
        }
    }
}

doc.Save(@"c:\temp\out.docx");

The issues you have found earlier (filed as WORDSNET-17235) have been fixed in this Aspose.Words for .NET 18.10 update and this Aspose.Words for Java 18.10 update.

@jepthy,

The feature (WORDSNET-17234) is now available in the latest version of Aspose.Words i.e. 19.7. Please use restartNum tag as shared in the following article.
Restarting List Numbering Dynamically

The issues you have found earlier (filed as WORDSNET-17234) have been fixed in this Aspose.Words for .NET 19.7 update and this Aspose.Words for Java 19.7 update.