Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to insert new Word Table at the placeholder paragraph ? #98

Open
ghost opened this issue Jan 14, 2022 · 8 comments
Open

Comments

@ghost
Copy link

ghost commented Jan 14, 2022

It doesn't seem possible to create a TypeResolver that return a docx4j Tbl object to insert at the placeholder paragraph.

Is there any other recommended way to do this ?

@dallanmc
Copy link

Can you give an example? Where is the data for the table coming from? Would it work if you create the table and then wrap it in a 'displayIf' comment?

@ghost
Copy link
Author

ghost commented Jan 20, 2022

Alas, the displayIf would not be enough.

Here is an example of what I would like to do.

The data for the table would come from the context, and would be able to generate a Tbl with an arbitrary nb of columns and lines, depending on the resolution.

This code fails with an error telling me i cannot insert a Table inside a paragraph in Docx, which is somewhat understandable.

I wonder if the TypeReolver is not the right direction, and if could tweak a CommentProcessor to replace the paragraph by the Table i want, or if it would necessitate something else... It seems to me that most of the scaffolding already exists in docx-stamper, but i don't know how to take advantage of it for my purpose.

public class MyContext {
    private String text;
    private MyTable table;

    public void setText(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public void setTable(MyTable table) {
        this.table = table;
    }

    public MyTable getTable() {
        return table;
    }
}

public class MyTable {
    private final List<String> headers;
    private final List<List<String>> records;

    public MyTable(
            List<String> headers,
            List<List<String>> records
    ) {
        this.headers = headers;
        this.records = records;
    }

    public List<List<String>> records() {
        return records;
    }

    public List<String> getHeaders() {
        return headers;
    }
}

class TableResolver implements ITypeResolver<MyTable, Tbl> {
    public static Tbl getTable(MyTable mytable, int writableWidthTwips ) {
        // Create my docx4j Tbl object here dynamically, with MyTable data
        return table;
    }

    @Override
    public Tbl resolve(WordprocessingMLPackage document, MyTable expressionResult) {
        int writableWidthTwips = document.getDocumentModel()
                .getSections().get(0).getPageDimensions().getWritableWidthTwips();
        return getTable(expressionResult, writableWidthTwips);
    }
}

public class Main {

    public static void main(String[] args) throws IOException {
        MyContext context = new MyContext();
        context.setText("string");
        context.setTable(new MyTable(
                List.of("Key1", "Key2"), // Table Headers
                List.of( // Table Rows
                        List.of("Key1 - Row1", "Key2 - Row1"),
                        List.of("Key1 - Row2", "Key2 - Row2")
                )
        ));

        InputStream template = Main.class.getClassLoader().getResourceAsStream("template.docx");            
        Path outputPath = Files.createTempFile("spiky", ".docx");
        System.out.println(outputPath);
        OutputStream out = Files.newOutputStream(outputPath);

        DocxStamperConfiguration configuration = new DocxStamperConfiguration();
        configuration.addTypeResolver(MyTable.class, new TableResolver());

        DocxStamper<MyContext> stamper = new DocxStamper<>(configuration);
        stamper.stamp(template, context, out);
        out.close();
    }

}

and the docx.template would contains something like

${text} // that would be replaced the value of 'text' in the context
${table} // that would be replaced the resolved Tbl from the MyTable object data in the context

@dallanmc
Copy link

Can I ask why the number of columns in the table is arbitrary? This seems like an unusual use case.

I still think it's possible though The biggest problem would be the table width and the column widths.

@ghost
Copy link
Author

ghost commented Jan 20, 2022

My use case is a pretty long report, in which in insert a lot of different kind of data, sometimes single values, sometimes images, sometimes tables.

Right now, i'm using the repeatRow comment to deal with all those tabular data, but it forces me to come up with that table with the right number of columns in the template, and come up with somewhat readable for every cell that will eventually be filled in.

I also have occasionally usecase where the table is vertical, and the headers in the first column, and every "repeat" should correspond to a new column.

for the width, i think i'm okay with tinkering with docx4j WordprocessingMLPackage class.

If this is an interesting feature for docx-stamper and it sounds feasible, i'm willing to explore and make a pull request when it works.

TL;DR: I'd like to replace a paragraph with a fully formed table to avoid to be too precise in the docx template.

@dallanmc
Copy link

dallanmc commented Jan 20, 2022

I'm thinking you could probably start off with a 1x2 table in the doc, stick some content in the first row and then second row (each row will only have one cell in the template). This would at least get you some formatting for the headers and the data which you wouldn't need to do programatically. It would also give you some fixed column widths.

After that it's a case of adding a commentProcessor to repeat the headers and the first row of data. After the first row is processed (i.e has all its variable names filled in), you can process the rows using the repeatTableRow code.
Screenshot 2022-01-20 at 10 00 57

Something like that (unsure if you even need those placeholders, but it at least gives you a style for any text that would go in there). As say, I think the hardest part will be getting the column widths right.

@ghost
Copy link
Author

ghost commented Jan 20, 2022

Thanks a lot, I'll try this out then.

@ghost
Copy link
Author

ghost commented Jan 26, 2022

It is working the way you proposed, there is indeed no need for placeholder, although keeping some text is useful to keep their style.
My implementation is not yet really robust, (because I don't really know yet the docx xml system, and keep getting surprised by complex wrappers elements).
Would you be interested in the code, to potentially include it ?
For now, i worked around the columns width problem by specifying the full table width at 100% inside the template.

@dallanmc
Copy link

Ah that's great that you got it working!
I'd don't need that code for my current project, but if I do, I'll contact you.

Thanks for sharing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant