The issue has been solved in the actual versions of PD4ML. Just give a try to any fully-functional evaluation version (or request a commercial one to evaluate from support pd4ml com).

> usual contract is that the one who opened a stream is the one responsible for closing it

You are right. But if we leave the output stream not closed, there is a probability the hosting framework writes some finalizing content/tags there, which corrupts the generated binary PDF data.

A safer (and recommended) approach would be to pass ByteArrayOutputStream to render() method. That would also let you measure generated PDF size and send correct Content-length HTTP header.