We're sorry Aspose doesn't work properply without JavaScript enabled.

# Generate report with Freemarker template

From your documentation I’ve seen that Aspose is working with LINQ. Do you know if there is a chance to generate Aspose with Freemarker template engine ?

This Topic is created by alexey.noskov using Email to Topic tool.

@brebDev In Aspose.Words you can use either standard mail merge template and Mail Merge feature to generate report or LINQ Reporting Engine and LINQ Syntax.

Could you please attach sample template used by your old system here for our reference?

@alexey.noskov Thanks for your reply. Unfortunately I cannot upload a real document here. But all our old documents are defined with Freemarker syntax, which is a bit different compared with LINQ. FOr example in Freemarker data is referenced like this: <<${object.somefield}>> instead of <<object.somefield>> like it is in LINQ. We choose Freemarker in the past, since it integrated ok with old document generation library. So probably if we want to move to Aspose document generation for old documents, all the templating syntax has to be replaced. I just hoped that maybe there is a way of doing it with Freemarker and Aspose. @brebDev Unfortunately, Aspose.Words does not support Freemarker syntax. But the syntax looks quite similar to LINQ Reporting Engine Syntax. So probably, you can use Find/Replace functionality to adopt your existing templates. For example if your source template has this: <<${object.somefield}>>


You can use code like the following to change this syntax to LINQ Reporting Engine Syntax:

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

FindReplaceOptions options = new FindReplaceOptions();
options.UseSubstitutions = true;

Regex regex = new Regex("<<\\$\\{([a-zA-Z\\.\\d]+)\\}>>"); doc.Range.Replace(regex, "<<[$1]>>", options);

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


in.docx (12.2 KB)
out.docx (9.5 KB)
Of course the regular expression is simplified and must be adjusted to fully fit your needs. Also, Freemarker syntax might have advanced construction, like conditions and loops, these construction should be changed manually.

Glad to see that things can be done like this.
We have around of 28 documents with multiple pages, and most of them have loops and conditional Freemarker logic which imply manual change and testing. Also most of the <<${object.somefield}>> , are defined as MergeFields in Word template. I think these has to be changed manually as well… @alexey.noskov really appreciate your support from the last days. That helped a lot. Thanks! @brebDev Please feel free to ask in case of any issues. We are always glad to help you. Hi @alexey.noskov . We’ve analyzed our existing documents and the impact of switching those to other templating engine syntax. That will have a big impact on our team. So our outcome was to leave the documents/code as it is(the old library + Freemarker is going to generate the documents) and reprocess the documents with Aspose. Of course the Aspose font configs will be there as described in the other cloned topic: Support Cambodian language. Basically the document is going to be processed twice… I’ve tested this hybrid solution and it works well. The document contains all the Cambodian language. The only issue that I see now is that the FONT_SUBSTITUTION callback is not triggered anymore(even if I remove DaunPenh font…) So the Aspose code will look like this: private byte[] reprocessWithAspose(byte[] wordDoc) throws Exception { LoadOptions lo = new LoadOptions(); lo.setFontSettings(buildFontSettings()); try (ByteArrayOutputStream output = new ByteArrayOutputStream(); OutputStream out = new BufferedOutputStream(output); InputStream is = new ByteArrayInputStream(wordDoc)) { Document doc = new Document(is, lo); doc.getFontInfos().setEmbedTrueTypeFonts(true); doc.getFontInfos().setEmbedSystemFonts(true); // Set this to false if it is supposed to edit the document. // If not set to false, only glyphs used in the document will be embedded. doc.getFontInfos().setSaveSubsetFonts(false); doc.getLayoutOptions().setTextShaperFactory(HarfBuzzTextShaperFactory.getInstance()); doc.setWarningCallback(warningCallback()); doc.save(out, SaveOptions.createSaveOptions(SaveFormat.DOCX)); return output.toByteArray(); } }  The only difference is that this code is not going to use anymore the ReportingEngine. Is this the reason why the FONT_SUBSTITUTION callback is not triggered ? Is there a way to make it work ?, since this will be really useful for us to create warnings if there will be any other language inserted by users, that native font is not supported by the system. Based on those warnings we can repeat the steps and install in resources those dedicated fonts. Do you have any other suggestions to flag these kind of missing fonts issues ? @brebDev In your code you are saving document to DOCX format, this is a flow format and no font substitutions are performed upon saving to flow formats. Aspose.Words simply writes the specified font name into the output document as it is specified in source document or in the code. The consumer application will select the required font in the environment where the document is consumed. Font substitution is performed upon building document layout by Aspose.Words, since it is required to measure texts upon this process. Document layout is build when you explicitly call Document.updatePageLayout method, or save in Fixed page formats, like PDF, XPS, Image etc. Ok, right the callback is activated only during the conversion. We’ve encountered another issue. So locally the Cambodian font was displayed, by using the DaunPehn font, which is free of using it on desktop machines. But for the Server, there are some license implications involved. So we want to avoid that, and we were thinking to use the opensource google font like this one for Khmer language: https://fonts.google.com/specimen/Khmer?subset=khmer . The issue is that Aspose is searching only for DaunPehn, even is this new one is configured in FontSettings and the callback got triggered every time… Is there a way to config Aspose to look for this new font(which is free), instead of default DaunPehn for Khmer language ? Thanks a lot! @brebDev I assume we are talking about conversion to fixed page formats like PDF. If so, Aspose.Words applies several font substitution rules, like described in our documentation: https://docs.aspose.com/words/net/manipulating-and-substitution-truetype-fonts/#font-availability-and-substitution First Aspose.Words looks for font specified in the document and if this font is not available substitution rules are applied. You can configure substitution rules to use the fonts available in your environment: https://reference.aspose.com/words/net/aspose.words.fonts/tablesubstitutionrule/ you can save the current configuration as XML, then edit it according to fonts availabe in your environment and then load the edited XML configuration. Hi @alexey.noskov. During this time another high priority work appeared, so this work was placed a side for a while. I’ve read the documentation that you’ve reference in the comment above. I’ve managed to use the opensource Khmer Google font, by using Aspose table substitution. The only issue that I’m having right now is that: For Word document those utility boxes are still appearing when I’m open the document with Word, but the pdf converted based on that Word is looking great. Not sure what is the reason for Word document, but I can copy the code here for both Word and pdf. Again pdf is looking great. Word code: private byte[] reprocessWithAspose(byte[] wordDoc, String docName) throws Exception { try (ByteArrayOutputStream output = new ByteArrayOutputStream(); OutputStream out = new BufferedOutputStream(output); InputStream is = new ByteArrayInputStream(wordDoc)) { LoadOptions lo = new LoadOptions(); lo.setFontSettings(buildFontSettings()); Document doc = new Document(is, lo); doc.getFontInfos().setEmbedTrueTypeFonts(true); doc.getFontInfos().setEmbedSystemFonts(true); // Set this to false if it is supposed to edit the document. // If not set to false, only glyphs used in the document will be embedded. doc.getFontInfos().setSaveSubsetFonts(false); doc.getLayoutOptions().setTextShaperFactory(HarfBuzzTextShaperFactory.getInstance()); doc.setWarningCallback(warningCallback(docName)); doc.updatePageLayout(); doc.save(out, SaveOptions.createSaveOptions(SaveFormat.DOCX)); out.flush(); return output.toByteArray(); } } private FontSettings buildFontSettings() throws IOException { // Fonts FontSettings fs = FontSettings.getDefaultInstance(); fs.setFontsSources( new FontSourceBase[]{ loadFont("/aspose/Khmer-Regular.ttf"), new SystemFontSource() } ); try(InputStream is = resourceLoader.getResource("classpath:aspose/table.xml").getInputStream()) { fs.getSubstitutionSettings().getTableSubstitution().load(is); } catch (Exception e) { log.error("Aspose - Font substitution table was not found"); } return fs; }  And substitution table looks like this: <TableSubstitutionSettings xmlns="Aspose.Words"> <SubstitutesTable> <Item OriginalFont="DaunPenh" SubstituteFonts="Khmer" /> </SubstitutesTable> </TableSubstitutionSettings>  And the Pdf code:  public byte[] convertToPdf(final byte[] wordDocBytes, final String fileName) throws DocumentGenerationException { // ... lazy load license if needed getLicenseInstance(); try(final InputStream is = new ByteArrayInputStream(wordDocBytes); final ByteArrayOutputStream os = new ByteArrayOutputStream() ) { LoadOptions lo = new LoadOptions(); lo.setFontSettings(buildFontSettings()); // Set options PdfSaveOptions options = new PdfSaveOptions(); options.setCompliance(PdfCompliance.PDF_17); // Load the Word document from memory Document doc = new Document(is, lo); doc.getLayoutOptions().setTextShaperFactory(HarfBuzzTextShaperFactory.getInstance()); doc.setWarningCallback(warningCallback(fileName)); // Convert Word to PDF doc.save(os, options); // return os.flush(); return os.toByteArray(); }catch(Exception e) { throw new DocumentGenerationException(DocumentGenerationException.ERR_PDF_CONVERTION, DocumentGenerationException.USER_MSG_GENERIC, "Issue when converting with Aspose for size="+wordDocBytes.length, e); } }  The only solution to make Word work, is to place the original: daunpenh.ttf font into buildFontSettings() method. But this is what I’m trying to avoid, I want to use these Google fonts since are free and can be used in commercial way as well. Any thoughts ? @brebDev Could you please attach a sample input and your output Word and PDF documents here for our reference? we will check the issue and provide you more information. If MS Word shows some characters as boxes, the reason might be that MS Word cannot find a right font to display these character. MS Word also uses it’s own font substitution mechanism if fonts are not available. The input example is: ម្រេចកំពត. But if I embed the Khmer(google font) into the document, shouldn’t Word use that one ?, since, if I’m embedding the daunpenh.ttf is working. Indeed when I’m opening the generated docx document with Word, Word is looking for DaunPenh font. On Windows machine I do not have that font installed. So the utility box appears instead(see attach: word-1.png). Afterwards, if I’m selecting any other font, for those utility boxes, Word will replace DaunPenh with Leelawadee UI font, which is provided by Windows(see attach: word-2.png). My question is why Word is not taking the embed font(Khmer font provided from Aspose code) ?? Also see attach the(pdf.png) , the pdf doc is working well with the table font substitution solution. Basically, what we’re trying to achieve is to generate docx/pdf documents with table substitution fonts for Windows/Linux and embed those, in order for our clients to use those documents regardless of their system fonts. Is that something we can achieve with both formats ?, or I should rely only on the pdf ? pdf.png (42.5 KB) word-1.png (33.5 KB) word-2.png (42.8 KB) @brebDev Could you please attach your actual documents - MS Word and PDF? It is difficult to analyze the issue using screenshots. @alexey.noskov Unfortunately I cannot attach full documents here, since these are highly confidential. But what I could do was to attach a simplified version of the template(to display only that specific field) So attached you’ll find: 1. The simplified template(Freemarker syntax) 2. Word/Pdf generated files based on this simplified template As a reminder. All our templates were designed initially with Freemarker, so in order to generate documents with Aspose we’re following these steps: 1. Generate Word doc with old-library and Freemarker 2. Reprocess the Word doc with Aspose 3. Convert the file from 2 ^^^ to Pdf with Aspose 4. Save both Word/Pdf files in our db doc.docx (4.5 MB) doc.pdf (27.4 KB) template.docx (99.0 KB) @brebDev Thank you for additional information. I have inspected your output DOCX document and see that in the document DaunPenh is explicitly used. <w:r w:rsidRPr="00D236FF"> <w:rPr> <w:rFonts w:ascii="DaunPenh" w:eastAsia="DaunPenh" w:hAnsi="DaunPenh" w:cs="DaunPenh" /> <w:b /> <w:sz w:val="28" /> <w:szCs w:val="28" /> <w:shd w:val="clear" w:color="auto" w:fill="FFFFFF" /> </w:rPr> <w:t>ម្រេចកំពត</w:t> </w:r>  So MS Word tries to use this font upon displayed the document. But this font is not embedded into the document (see fonttable.xml) <w:font w:name="DaunPenh"> <w:charset w:val="00" /> <w:family w:val="auto" /> <w:pitch w:val="default" /> </w:font>  As well as Khmer font is also not embedded, since it is not used in the document. If you use the Khmer font in your document and configure Aspose.Words to embed fonts into the DOCX document, MS Word will use the embedded font. For example see the following simple code (just for demonstration): Document doc = new Document(); DocumentBuilder builder = new DocumentBuilder(doc); // Specify font sources. // The specified folder font source contains Khmer font. doc.setFontSettings(new FontSettings()); doc.getFontSettings().setFontsSources(new FontSourceBase[]{new SystemFontSource(), new FolderFontSource("C:\\Temp\\fonts", true)}); // Explicitly use Khmer font in the document. builder.getFont().setName("Khmer"); builder.write("ម្រេចកំពត"); // Configure Aspose.Words to embed fonts into the document. doc.getFontInfos().setEmbedTrueTypeFonts(true); // Save as DOCX and PDF doc.save("C:\\Temp\\out.docx"); doc.save("C:\\Temp\\out.pdf");  Here are output documents generated by this code: out.docx (31.2 KB) out.pdf (6.3 KB) If you inspect the output DOCX document, you will notice that Khmer font is used in the document and is embedded: <w:r> <w:rPr> <w:rFonts w:ascii="Khmer" w:eastAsia="Khmer" w:hAnsi="Khmer" w:cs="Khmer" /> </w:rPr> <w:t>ម្រេចកំពត</w:t> </w:r>  <w:font w:name="Khmer"> <w:charset w:val="00" /> <w:family w:val="auto" /> <w:pitch w:val="default" /> <w:embedRegular r:id="rId1" w:fontKey="{46A5CC74-A263-4E37-9BE3-0BDDA2141701}" /> </w:font>  Hi @alexey.noskov. Based on your example, I’ve tried to play with different cases, but unfortunately this example cannot be adapted to our case…So what I’ve discovered is: Even though the table font substitution is defined, is not working if you comment the following line: builder.getFont().setName("Khmer"); I was expecting that Aspose will interpret each font from the original doc, and will replace only the one which is defined in the table. We cannot define whole document to be in Khmer, since the original doc supports also other fonts… In this case the utility boxes appears. @alexey.noskov Again, I’m looking for a generic solution for Word which behaves like PDF doc generation(which is working perfect). So to cut it short, based on the document attached, that contains in the first row: Arial font, and in the second row the DaunPehn font. Could you define a solution that will: 1. read the provided document 2. generate new doc by using the table font substitution 3. make sure that the Word doc generated will have the first row as it was: Arial and the second will be in Khmer. Also Khmer font will be embed in the generated doc. Is that technically possible with Aspose ? Or is just only working for PDF ? If it helps I’ve attached the initial Freemarker template as well. If you want to create the full flow, I guess this should be adapted for LINQ. But anyway, this step is not necessary… doc_generated.docx (4.5 MB) template.docx (99.1 KB) @brebDev Aspose.Words font substitution mechanist works only upon rendering document to fixed page formats, like PDF, XPS, Image etc, since upon rendering Aspose.Words requires the fonts to be physically available to use their metrics to build the document layout. On other hand, when you save the document flow formats, like DOCX, HTML, DOC, RTF, etc. font substitution is not required, since the consumer applications, like MS Word, have their own font substitution mechanisms. So Aspose.Words does not replace fonts in the flow document, but simply writes the font name, which s specified in the source document or is applied in the code. Hi @alexey.noskov. Ok good to know that, we’ll rely then only on the PDF documents. So I’ve tested the code above in these environements: 1. Windows 10 Enterprise(Aws Windows Workspace) - OK 2. Amazon Linux 2(Aws Windows Workspace) - OK * here it was a problem with Chinese characters 3. deployed the code in DEV env(Linux) - FAILURE So in Dev env it failed with the following stacktrace:  Caused by: java.lang.UnsatisfiedLinkError: 'long com.aspose.words.shaping.harfbuzz.HB.hb_font_create_from_data(byte[], int)' at com.aspose.words.shaping.harfbuzz.HB.hb_font_create_from_data(Native Method) ~[aspose-words-21.11-shaping-harfbuzz-plugin.jar!/:na] at com.aspose.words.shaping.harfbuzz.zzXou.zzWuo(Unknown Source) ~[aspose-words-21.11-shaping-harfbuzz-plugin.jar!/:na] at com.aspose.words.shaping.harfbuzz.zzXNO.<init>(Unknown Source) ~[aspose-words-21.11-shaping-harfbuzz-plugin.jar!/:na] at com.aspose.words.shaping.harfbuzz.HarfBuzzTextShaperFactory.getTextShaper(Unknown Source) ~[aspose-words-21.11-shaping-harfbuzz-plugin.jar!/:na] at com.aspose.words.BasicTextShaperCache.getTextShaper(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzS5.zzWuo(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzS5.zzWuo(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzEk.zzXXW(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzEk.zzXgf(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzWzm.zzXgf(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZOi.zzYng(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zz8y.zzsf(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zz8y.zzXb(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZOi.zzXb(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzXX0.zzWA9(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzXX0.zzYnz(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzXX0.zzNY(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzif.zzWuo(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzif.zzX8D(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZPW.zzZNC(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZPW.zzYnz(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZPW.zzWg7(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzY1x.zzWuo(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzY1x.zzWuo(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZxT.zzy5(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZxT.zzXou(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZxT.zzWuo(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZBg.zzXM7(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZBg.zzYzK(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zz0F.zzZcY(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzWeZ.zzXou(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzWeZ.zzXNO(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzXIC.zzXgf(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZBg.zzYzK(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zz0F.zzZcY(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzVWZ.zzYzK(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzWoC.zzXrB(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzYhl.zzWKH(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.Document.updatePageLayout(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzXk6.zzYl9(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzYoc.zzYla(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZ0K.zzWG9(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZ0K.zzXou(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZ0K.zzWuo(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.zzZ0K.zzYij(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.Document.zzVTk(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.Document.zzXou(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.Document.zzWuo(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.Document.zzWuo(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at com.aspose.words.Document.save(Unknown Source) ~[aspose-words-21.11-jdk17.jar!/:21.11.0] at org.wipo.elisbon.document.DocumentGenerationServiceImpl.convertToPdf(DocumentGenerationServiceImpl.java:1942) ~[business-0.0.1-SNAPSHOT.jar!/:0.0.1-SNAPSHOT]  It seems is failing at the code that I’ve posted above at the PDF sections, at the line where is saving the document: doc.save(os, options);  I’m using the last version of Aspose: 23.1 and java: java-11-amazon-corretto.x86_64 Pom file in Maven looks like this: <properties> <version.aspose>23.1</version.aspose> </properties> <dependency> <groupId>com.aspose</groupId> <artifactId>aspose-words</artifactId> <version>${version.aspose}</version>
<lassifier>jdk17</classifier>
</dependency>

<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>${version.aspose}</version> <classifier>javadoc</classifier> </dependency> <dependency> <groupId>com.aspose</groupId> <artifactId>aspose-words</artifactId> <version>${version.aspose}</version>
<classifier>shaping-harfbuzz-plugin</classifier>
</dependency>


I’ve looked on other posts related to this topic, but doesn’t seem to help. In most of them the outcome was to upgrade to latest version.

Could please take a look on this one ?

Sorry, for the XML format…Somehow is not being processed really well.

Thanks,
Luigi