Hi guys!
Could you please help me? My goal is to use tinyMSE as an editor and aspose for document export. I want to achieve maximum similarity between what the user sees during editing and the document that aspose exports.
So my editor looks like this:
As you can see, it consists of text divided into numbered parts. We have a heading, parent and child document items. Now their structure is quite simple and looks like this:
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.util.UUID;
@RequiredArgsConstructor
@Getter
@Setter
public class DocItem {
private UUID id;
private String content;
private Integer depth;
private String number;
}
Content is the html string that tinyMSE sends to the backend. I’ll give an example of content a little bit below. Depth for the heading is 0 for parent 1 and for the child element 2. The number is recalculated on the backend and is always correct. The frontend takes the value of this field to display the document item number in the editor (I haven’t found a way to use this field with aspose, so I use document item depth to determine the numbering when exporting a document).
Additionally, I have a title page that will contain some text and all other content should be attached to the document after the title page. This is a simple example of a title page:
title-page.docx (34,4 КБ)
Here is an example of the code that I use to try to achieve the desired result:
import com.aspose.words.Document;
import com.aspose.words.DocumentBuilder;
import com.aspose.words.FieldStart;
import com.aspose.words.HeaderFooterType;
import com.aspose.words.ImportFormatMode;
import com.aspose.words.NodeType;
import com.aspose.words.Paragraph;
import com.aspose.words.ParagraphAlignment;
import ru.vtb.dc.agreement.service.convert.AsposeUtils;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.apache.commons.text.StringEscapeUtils.unescapeHtml4;
import static ru.vtb.dc.agreement.service.convert.exporter.impls.StandardDocumentExporter.CUSTOM_LIST_STYLE;
import static ru.vtb.dc.agreement.service.document.variable.DocumentVariablesService.UUID_PATTERN;
public class Main {
public static void main(String[] args) throws Exception {
final var doc = getDocTemplate();
final var builder = new DocumentBuilder(doc);
ExportTemplate exportTemplate = new ExportTemplate();
exportTemplate.init();
addPageNumbering(builder);
renderDocItems(builder, exportTemplate);
setDocItemNumbering(exportTemplate, builder.getDocument());
doc.save("j:\\result.docx");
}
private static Document getDocTemplate() throws Exception {
return new Document(ClassLoader.getSystemResourceAsStream("title-page.docx"));
}
private static void addPageNumbering(DocumentBuilder builder) {
builder.moveToHeaderFooter(HeaderFooterType.FOOTER_PRIMARY);
builder.insertField("PAGE", "");
builder.getCurrentParagraph().getParagraphFormat().setAlignment(ParagraphAlignment.RIGHT);
}
private static void renderDocItems(DocumentBuilder builder, ExportTemplate exportTemplate) throws Exception {
final var docItems = exportTemplate.getDocItems();
for (DocItem item : docItems) {
var htmlContent = unescapeHtml4(item.getContent());
final var document = new Document();
final var documentBuilder = new DocumentBuilder(document);
documentBuilder.insertHtml(htmlContent, true);
AsposeUtils.setListPaddings(document, 40);
insertField(item, document);
builder.insertDocument(document, ImportFormatMode.USE_DESTINATION_STYLES);
}
}
private static void insertField(DocItem docItem, Document doc) {
var documentBuilder = new DocumentBuilder(doc);
AsposeUtils.toStream(doc.getChildNodes(NodeType.PARAGRAPH, true), Paragraph.class)
.forEach(paragraph -> {
documentBuilder.moveTo(paragraph);
try {
documentBuilder.insertField(docItem.getId().toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
public static void setDocItemNumbering(ExportTemplate exportTemplate, Document documentTemplate) {
final var listStyle = documentTemplate.getStyles().get(CUSTOM_LIST_STYLE);
final var asposeList = documentTemplate.getLists().add(listStyle);
// If I don't do this, I get numbers in each table cell. Surely there is some simpler way to avoid this?
final var docItemUuids = exportTemplate.getDocItems()
.stream()
.map(DocItem::getId)
.collect(Collectors.toSet());
AsposeUtils.toStream(documentTemplate.getChildNodes(NodeType.FIELD_START, true), FieldStart.class).forEach(fieldStart -> {
final var fieldCode = fieldStart.getField().getFieldCode();
if (fieldCode != null && fieldCode.matches(UUID_PATTERN) && docItemUuids.contains(UUID.fromString(fieldCode))) {
docItemUuids.remove(UUID.fromString(fieldCode));
final var docItem = exportTemplate.getDocItems()
.stream()
.filter(item -> fieldCode.equals(String.valueOf(item.getId())))
.findFirst()
.get();
final var paragraph = (Paragraph) fieldStart.getParentNode();
paragraph.getListFormat().setList(asposeList);
paragraph.getListFormat().setListLevelNumber(docItem.getDepth());
}
});
}
}
This is an example of input data from the tinyMSE editor that I have no control over.
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@RequiredArgsConstructor
@Getter
@Setter
public class ExportTemplate {
List<DocItem> docItems = new ArrayList<>();
public void init() {
DocItem docItem = new DocItem();
docItem.setDepth(0);
docItem.setId(UUID.randomUUID());
docItem.setNumber("1.");
docItem.setContent("INITIATING UNITS (header item)");
docItems.add(docItem);
DocItem docItem2 = new DocItem();
docItem2.setDepth(1);
docItem2.setId(UUID.randomUUID());
docItem2.setNumber("1.1.");
docItem2.setContent("<table style="width: 93.2436%; height: 100px;">\n<thead>\n<tr style="height: 20px;">\n<th style="width: 37.8517%;"><strong>Initiating units</strong></th>\n<th style="width: 26.5985%;">__________</th>\n<th style="width: 35.0384%;">_________</th>\n</tr>\n</thead>\n<tbody>\n<tr style="height: 20px;">\n<td style="width: 37.8517%;"><strong>Project team members</strong></td>\n<td style="width: 26.5985%;"><strong>Department name</strong></td>\n<td style="width: 35.0384%;"><strong>Full name of the project team member</strong></td>\n</tr>\n<tr style="height: 20px;">\n<td style="width: 37.8517%;"><em><strong>Leader</strong></em></td>\n<td style="width: 26.5985%;">__________</td>\n<td style="width: 35.0384%;">__________</td>\n</tr>\n<tr style="height: 20px;">\n<td style="width: 37.8517%;"><em><strong>Product Manager</strong></em></td>\n<td style="width: 26.5985%;">__________</td>\n<td style="width: 35.0384%;">__________</td>\n</tr>\n<tr style="height: 20px;">\n<td style="width: 37.8517%;"><em><strong>Ticket Number</strong></em></td>\n<td style="width: 26.5985%;"><em>__________</em></td>\n<td style="width: 35.0384%;">__________</td>\n</tr>\n</tbody>\n</table>");
docItems.add(docItem2);
DocItem docItem3 = new DocItem();
docItem3.setDepth(2);
docItem3.setId(UUID.randomUUID());
docItem3.setNumber("1.1.1.");
docItem3.setContent("<p>Children item</p>");
docItems.add(docItem3);
DocItem docItem4 = new DocItem();
docItem4.setDepth(1);
docItem4.setId(UUID.randomUUID());
docItem4.setNumber("1.2.");
docItem4.setContent("<p>Some Text</p><ol>\n<li>&nbsp;first number\n<ul>\n<li>&nbsp;second bullet</li>\n</ul>\n</li>\n<li>&nbsp;second number\n<ol>\n<li>&nbsp;third numer</li>\n</ol>\n</li>\n</ol><p>Some Text</p>");
docItems.add(docItem4);
DocItem docItem5 = new DocItem();
docItem5.setDepth(0);
docItem5.setId(UUID.randomUUID());
docItem5.setNumber("2.");
docItem5.setContent("CLIENTS INFO (second header item)");
docItems.add(docItem5);
}
}
If you run this code you will get the following:
result.docx (39,4 КБ)
The issues that I see are:
- Incorrect numbering. Point 1.1. got inside the first cell of the table. I would like it to be outside the table, and for the table itself to have an indent slightly to the right of the numbering.
- No indents for child docItems of the document. DocItem 1.2 with the same indentation as clause DocItem 1.
- The content of docItem 1.2. does not look at all like in the editor. Incorrect indents and numbering of the nested sheet. Instead of 2.1) I see 1)
- No page numbering.
- Large gap between the content and the title page.
Guys, sorry for writing such a long post, but I’m trying to figure out your product and I’m not very familiar with it yet. If you can help me answer some of my questions that would be great!
If you need more information or examples, let me know!
Thanks in advance!