Spire.Doc is a professional Word .NET library specifically designed for developers to create, read, write, convert and print Word document files from any .NET platform (C#, VB.NET, ASP.NET, .NET Core) and Java applications (J2SE and J2EE) with fast and high quality performance.

Mon Nov 29, 2021 2:25 pm

I load a docx template from Stream

Code: Select all
        final Document doc = new Document();
        doc.loadFromStream(input, FileFormat.Docx);

and fill a table with column and data using Spire.Office 4.11.3 (see document Etude.docx, page 22 & 23).

After, I tried to save the document directly to PDF

Code: Select all
        final UUID uuid = UUID.randomUUID();
        doc.saveToFile(uuid.toString() + ".pdf", FileFormat.PDF); // see fab534ab-e0be-47a3-93ea-ee8b13544050_saveToFile.pdf


but the tables are not generated correctly : see fab534ab-e0be-47a3-93ea-ee8b13544050_saveToFile2.pdf (the second column is too big) !

If I try to save to docx first, and after loadFromStream from the generated docx file, and try to export by this way to PDF : the generated PDF and the tables are not the same, but still not the result expected.

Code: Select all
        doc.saveToFile(uuid.toString() + ".docx", FileFormat.Docx); // see fab534ab-e0be-47a3-93ea-ee8b13544050.docx

        try {
            final Document doc2 = new Document();
            doc2.loadFromStream(new FileInputStream(uuid.toString() + ".docx"), FileFormat.Docx);
            doc2.saveToFile(uuid.toString() + "_2.pdf", FileFormat.PDF); // see fab534ab-e0be-47a3-93ea-ee8b13544050_saveToFile2.pdf

        } catch(FileNotFoundException e) {

        }
You do not have the required permissions to view the files attached to this post.

QuentinSup
 
Posts: 37
Joined: Mon Oct 18, 2021 9:18 am

Tue Nov 30, 2021 3:31 am

Hello,

Thanks for your inquiry!

I noticed that the input file “Etude.docx” is totally different with the “e0fab625-c77a-4650-971f-5d2e39c8bc67.docx” since you have inserted some data and change the table format. Sorry that if there is only your input file, and without your specific operation code, I'm afraid I can't reproduce your issue accurately. Please provide us with your completely code (a java class is better) for further investigation. Thanks in advance.

Sincerely,
Marcia
E-iceblue support team
User avatar

Marcia.Zhou
 
Posts: 738
Joined: Wed Nov 04, 2020 2:29 am

Tue Nov 30, 2021 10:56 am

Hi,

here an extract of the code used to manipulate table and cells :


import com.example.demo.CellTypeEnum;
import com.example.demo.CellValue;
import com.spire.doc.*;
import com.spire.doc.documents.BorderStyle;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.fields.TextRange;
import com.spire.doc.formatting.CharacterFormat;

import java.awt.*;
import java.io.*;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

public class ExportPdfIndexOutOfBounds {

protected static NumberFormat currencyFormater;
protected static NumberFormat percentFormater;
protected static NumberFormat currencyCompactFormater;
protected static boolean formatCurrencyWithSymbol = true;
protected static NumberFormat numberFormater;
public static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");

{
percentFormater = NumberFormat.getPercentInstance(Locale.FRANCE);
percentFormater.setMaximumFractionDigits(2);
currencyFormater = NumberFormat.getCurrencyInstance(Locale.FRANCE);
currencyCompactFormater = DecimalFormat.getCompactNumberInstance(Locale.FRANCE, NumberFormat.Style.SHORT);
numberFormater = NumberFormat.getNumberInstance(Locale.FRANCE);
numberFormater.setMaximumFractionDigits(2);
}

public static void main(String[] args) throws Exception {

String inputFile = "document.docx";

//create word document
FileInputStream stream1 = new FileInputStream(new File(inputFile));
Document document = new Document();
document.loadFromStream(stream1, FileFormat.Auto);

Map<String, TagValue<?>> rows = new HashMap<>();

rows.put("period", new TagValue<>(List.of("1", "2", "3", "Total"), TagValueTypeEnum.ROW));
rows.put("rentAmount", new TagValue<>(List.of("1000", "2000", "3000", "6000"), TagValueTypeEnum.ROW));
/**
* @TODO More example
*/
fillTables(rows, document);

ByteArrayOutputStream stream = new ByteArrayOutputStream();
document.saveToStream(stream, FileFormat.PDF);

FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream("topdf.pdf");
fileOutputStream.write(stream.toByteArray());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* Traite les tableaux dynamiques d'un document WORD
*
* @param mapList values
* @param doc document
*/
public static void fillTables(Map<String, TagValue<?>> mapList, Document doc) {
if (doc.getSections() != null && doc.getSections().getCount() > 0) {
for (Section section : (Iterable<Section>) doc.getSections()) {
if (section.getTables() != null && section.getTables().getCount() > 0) {
for (int i = 0; i < section.getTables().getCount(); i++) {
int maxColumnCount = fillTable(mapList, section.getTables().get(i));
if (maxColumnCount > 0) {
formatTable(section.getTables().get(i), maxColumnCount);
}
}
}
}
}
}

/**
* Complète un tableau WORD par les valeurs correspondantes de la liste formatée et supprime les tags traités du référentiel de tags si non null
*
* @param map values
* @param table table
*/
private static int fillTable(Map<String, TagValue<?>> map, Table table) {
boolean isReplaced;
int maxColumnCount = 0;

// replace values from map list
for (Object o : table.getRows()) {
final TableRow row = ((TableRow) o);
for (Map.Entry<String, TagValue<?>> entry : map.entrySet()) {
final String formatedKey = "${" + entry.getKey() + "[n]}";
if (row.getCells().getCount() > 0) {
if (row.getCells().get(row.getCells().getCount() - 1).getParagraphs().get(0).getText()
.contains(formatedKey))
{
isReplaced = false;
final List<?> values = (List<?>) entry.getValue().getValue();
maxColumnCount = Math.max(values.size(), maxColumnCount);
for (Object v : values) {
final String value = formatValue(mapToTagValue(v));
if (!isReplaced) {
row.getCells().get(row.getCells().getCount() - 1).getFirstParagraph()
.replace(formatedKey, value, true, true);
isReplaced = true;
} else {
row.addCell().addParagraph().appendText(value);
}
}
}
}
}
}
return maxColumnCount;
}

/**
* return appropriate representation oif tag value depending on type
*
* @param o Object value
*/
protected static TagValue<?> mapToTagValue(Object o) {

if (o instanceof TagValue) {
return (TagValue<?>) o;
}

if (o instanceof CellValue) {
final CellValue<?> cellValue = (CellValue<?>) o;
if (cellValue.getType() == CellTypeEnum.CURRENCY) {
return new TagValue<>(cellValue.getValue(), TagValueTypeEnum.CURRENCY);
}
if (cellValue.getType() == CellTypeEnum.CURRENCY_COMPACT) {
return new TagValue<>(cellValue.getValue(), TagValueTypeEnum.CURRENCY_COMPACT);
}
if (cellValue.getType() == CellTypeEnum.NUMBER) {
return new TagValue<>(cellValue.getValue(), TagValueTypeEnum.NUMBER);
}
if (cellValue.getType() == CellTypeEnum.PERCENT) {
return new TagValue<>(cellValue.getValue(), TagValueTypeEnum.PERCENT);
}
if (cellValue.getType() == CellTypeEnum.DATE) {
return new TagValue<>(cellValue.getValue(), TagValueTypeEnum.DATE);
}
}

if (o instanceof Long || o instanceof Integer || o instanceof Float) {
return new TagValue<>(o, TagValueTypeEnum.NUMBER);
}

if (o instanceof Double) {
return new TagValue<>(o, TagValueTypeEnum.PERCENT);
}

if (o instanceof List) {
return new TagValue<>(((List<?>) o).stream().map(ExportPdfIndexOutOfBounds::mapToTagValue).collect(Collectors.toList()),
TagValueTypeEnum.LIST);
}

return new TagValue<>(o, TagValueTypeEnum.TEXT);

}

public enum TagValueTypeEnum {
TEXT, CURRENCY, CURRENCY_COMPACT, NUMBER, PERCENT, DATE, IMAGE_BYTE, IMAGE_BASE64, IMAGE_PATH, MERGE_FIELD, CHART, LIST, ROW
}

public static class TagValue<T> {
private T value;
private TagValueTypeEnum type = TagValueTypeEnum.TEXT;

public TagValue(T value) {
this.value = value;
}

public TagValue(T value, TagValueTypeEnum type) {
this.value = value;
this.type = type;
}

public boolean isPrimitive() {
return type.equals(TagValueTypeEnum.TEXT) || type.equals(TagValueTypeEnum.DATE) || type.equals(
TagValueTypeEnum.CURRENCY) || type.equals(TagValueTypeEnum.CURRENCY_COMPACT) || type.equals(
TagValueTypeEnum.NUMBER) || type.equals(TagValueTypeEnum.PERCENT);
}

public T getValue() {
return value;
}

public void setValue(T value) {
this.value = value;
}

public TagValueTypeEnum getType() {
return type;
}

public void setType(TagValueTypeEnum type) {
this.type = type;
}
}

/**
* Mise en forme des lignes d'un tableau WORD
*
* @param table table
* @param maxColumnCount max column count
*/
private static void formatTable(Table table, int maxColumnCount) {
TableRow row;
TableCell cell, firstCell;
Paragraph cellParagraph, firstParagraph;
TextRange cellText;
Color borderColor;

// format table rows
final int totalColumnCount = maxColumnCount;
int columnCount;
int rowCount = 1;
final PreferredWidth tableWidth = new PreferredWidth(WidthType.Percentage, (short) 100);
table.setPreferredWidth(tableWidth);
table.getTableFormat().getPaddings().setLeft(5);
table.getTableFormat().getPaddings().setRight(5);
float columnWidth =
(table.getWidth() - table.getFirstRow().getCells().get(0).getWidth()) / (totalColumnCount + 1);
table.setDefaultColumnWidth(columnWidth);
for (Object o1 : table.getRows()) {
row = ((TableRow) o1);

while (row.getCells().getCount() <= totalColumnCount) {
row.addCell().addParagraph().appendText("");
}

firstCell = row.getCells().get(1);

firstParagraph = (Paragraph) firstCell.getFirstParagraph();
final CharacterFormat characterFormatModel = firstParagraph.getChildObjects().getCount() > 0 ?
((TextRange) firstParagraph.getChildObjects().get(0)).getCharacterFormat() :
firstParagraph.getBreakCharacterFormat();

columnCount = 1;
for (Object o2 : row.getCells()) {

if (columnCount == 1) {
columnCount++;
continue;
}

borderColor = rowCount == 1 ? Color.WHITE : Color.BLACK;
cell = (TableCell) o2;

// TODO -> appliquer le format de la première ligne cell et paragraph à la dernière colonne de chaque ligne
// condition : rowCount != 2 && columnCount == row.getCells().getCount()

if (columnCount == totalColumnCount + 1) {
cell.setCellWidth((float) (columnWidth * 1.20), CellWidthType.Point);
} else {
cell.setCellWidth(columnWidth, CellWidthType.Point);
}

// cell format
cell.getCellFormat().setBackColor(firstCell.getCellFormat().getBackColor());
cell.getCellFormat().setVerticalAlignment(firstCell.getCellFormat().getVerticalAlignment());
cell.getCellFormat().setVerticalMerge(firstCell.getCellFormat().getVerticalMerge());
cell.getCellFormat().setTextWrap(false);

// right border all 5 year period ; no border for line 2
cell.getCellFormat().getBorders().getBottom()
.setBorderType(firstCell.getCellFormat().getBorders().getBottom().getBorderType());
cell.getCellFormat().getBorders().getTop()
.setBorderType(firstCell.getCellFormat().getBorders().getTop().getBorderType());
cell.getCellFormat().getBorders().getLeft().setBorderType(
rowCount != 2 && columnCount <= 2 ? BorderStyle.Basic_Thin_Lines : BorderStyle.Cleared);
cell.getCellFormat().getBorders().getRight().setBorderType(
rowCount != 2 && ((columnCount - 1) % 5 == 0 || columnCount == row.getCells().getCount()) ?
BorderStyle.Basic_Thin_Lines : BorderStyle.Cleared);
cell.getCellFormat().getBorders().getRight().setColor(borderColor);
cell.getCellFormat().getBorders().getLeft().setColor(borderColor);

// paragraph format
cellParagraph = cell.getParagraphs().getCount() > 0 ? (Paragraph) cell.getFirstParagraph() :
cell.addParagraph();

cellParagraph.applyStyle(firstParagraph.getStyleName());
cellParagraph.getFormat().setHorizontalAlignment(firstParagraph.getFormat().getHorizontalAlignment());
cellParagraph.getFormat().setTextAlignment(firstParagraph.getFormat().getTextAlignment());
cellParagraph.getFormat().setBeforeSpacing(firstParagraph.getFormat().getBeforeSpacing());
cellParagraph.getFormat().setAfterSpacing(firstParagraph.getFormat().getAfterSpacing());
cellText = cellParagraph.getChildObjects().getCount() > 0 ?
(TextRange) cellParagraph.getChildObjects().get(0) : cellParagraph.appendText("");

if (characterFormatModel != null) {
cellText.getCharacterFormat().setTextColor(characterFormatModel.getTextColor());
cellText.getCharacterFormat().setFontName(characterFormatModel.getFontName());
cellText.getCharacterFormat().setFontSize(characterFormatModel.getFontSize());
cellText.getCharacterFormat().setBold(characterFormatModel.getBold());
cellText.getCharacterFormat().setUnderlineStyle(characterFormatModel.getUnderlineStyle());
}

if (cellText.getText().isBlank()) {
cellText.getCharacterFormat().setTextColor(Color.WHITE);
// Mandatory to have a character (not space) for the style to be applied correctly
cellText.setText("-");
cellText.getCharacterFormat().setFontSize(1);
}

columnCount++;
}
//row.setHeight(initialRowHeight);

rowCount++;
}
}

/**
* Format tag value ; if null, return ""
*
* @param tag value to format
*/
protected static String formatValue(TagValue<?> tag) {
Object tagValue = tag.getValue() != null ? tag.getValue() : null;
if (tagValue != null) {
if (TagValueTypeEnum.CURRENCY == tag.getType()) {
tagValue = currencyFormat(String.valueOf(tagValue), formatCurrencyWithSymbol);
} else if (TagValueTypeEnum.CURRENCY_COMPACT == tag.getType()) {
tagValue = currencyCompactFormat(String.valueOf(tagValue), formatCurrencyWithSymbol);
} else if (TagValueTypeEnum.NUMBER == tag.getType()) {
tagValue = numberFormat(tagValue);
} else if (TagValueTypeEnum.PERCENT == tag.getType()) {
tagValue = percentFormat(tagValue);
} else if (TagValueTypeEnum.DATE == tag.getType()) {
assert tagValue instanceof Date;
tagValue = dateFormat.format((Date) tagValue);
}
}
return getFixedValue(tagValue);
}

/**
* Return string value with fixed chars
*
* @param tagValue the object
* @return the fixed string value
*/
protected static String getFixedValue(Object tagValue) {
return tagValue == null ? "" :
(tagValue instanceof String ? (String) tagValue : tagValue.toString()).replace((char) 8239, (char) 160);
}

/**
* Formattage du prix
*
* @param amount value to format
* @param currencySymbol boolean
* @return String
*/
protected static String currencyFormat(String amount, boolean currencySymbol) {
return currencyFormat(amount, currencySymbol, currencyFormater);
}

/**
* Formattage du prix
*
* @param amount value to format
* @param currencySymbol boolean
* @return String
*/
protected static String currencyCompactFormat(String amount, boolean currencySymbol) {

NumberFormat numberFormat;
if (isNullOrZeroValue(amount)) {
numberFormat = currencyFormater;
} else {
final double value = Double.parseDouble(amount);
if (value > -1000000. && value < 1000000.) {
numberFormat = currencyFormater;
} else {
numberFormat = (NumberFormat) currencyCompactFormater.clone();
double absValue = Math.abs(value);
while (absValue > 1000) {
double r = absValue % 1000;
if (r != 0) {
numberFormat.setMinimumFractionDigits(String.valueOf(r).length());
break;
}
absValue = absValue / 1000;
}
}
}

String value = currencyFormat(amount, false, numberFormat);
if(currencySymbol) {
value = value.concat(Currency.getInstance(Locale.FRANCE).getSymbol());
}
return value;
}

public static boolean isNullOrZeroValue(String value) {
return (value == null || "".equals(value) || "null".equals(value));
}

/**
* Formattage du prix
*
* @param amount value to format
* @param currencySymbol boolean
* @return String
*/
protected static String currencyFormat(String amount, boolean currencySymbol, final NumberFormat numberFormat) {

NumberFormat format = numberFormat;

if (isNullOrZeroValue(amount)) {
return format.format(0);
}

if (!currencySymbol) {
if (format instanceof DecimalFormat) {
final DecimalFormat decimalFormat = (DecimalFormat) format.clone();
final DecimalFormatSymbols decimalFormatSymbols = decimalFormat.getDecimalFormatSymbols();
decimalFormatSymbols.setCurrencySymbol("");
decimalFormat.setDecimalFormatSymbols(decimalFormatSymbols);
format = decimalFormat;
}
}

return format.format(Double.parseDouble(amount));

}

/**
* Formattage d'un nombre
*
* @param number value to format
* @return String
*/
protected static String numberFormat(Object number) {
if (number == null || "".equals(number) || "null".equals(number)) {
return numberFormater.format(0);
}
return numberFormater.format(number);
}

/**
* Formattage d'un nombre
*
* @param number value to format
* @return String
*/
protected static String percentFormat(Object number) {
if (number == null || "".equals(number) || "null".equals(number)) {
return percentFormater.format(0);
}
return percentFormater.format(number);
}

}

[/code]

QuentinSup
 
Posts: 37
Joined: Mon Oct 18, 2021 9:18 am

Wed Dec 01, 2021 5:58 am

Hello,

Thanks for sharing more information!

I have reproduced your issue and logged it in our issue tracking system with the ticket SPIREDOC-7052 for further investigation.

We will let you know if there is any update. Sorry for the inconvenience caused.

Sincerely,
Marcia
E-iceblue support team
User avatar

Marcia.Zhou
 
Posts: 738
Joined: Wed Nov 04, 2020 2:29 am

Tue Jan 25, 2022 10:13 am

Hello,

Thanks for your patience!

Glad to inform you that we just released Spire.Office for Java 5.1.5 Version:5.1.5 which fixes the issue SPIREDOC-7052.

Please download the fix version from the following links to test.

Website link: https://www.e-iceblue.com/Download/office-for-java.html

Sincerely,
Marcia
E-iceblue support team
User avatar

Marcia.Zhou
 
Posts: 738
Joined: Wed Nov 04, 2020 2:29 am

Return to Spire.Doc

cron