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.