christopher-ramirez / secretary

Take the power of Jinja2 templates to OpenOffice and LibreOffice.
Other
190 stars 48 forks source link

Initial image support in secretary #9

Closed timostrunk closed 10 years ago

timostrunk commented 10 years ago

This is not really a pull request, but rather a "Hey, please check what I did, maybe you like it" - request.

I recently found secretary and wanted to use it to generate some reports in our software. First of all: Thanks for your effort! Using it is painless and it simply works! However most of our results are images (plots, renderings, etc.) and so secretary required the ability to also insert images dynamically (i.e. without a placeholder image, because we do not a-priori know the number or size of images in a for-loop).

The pull request includes initial code to support image inclusion. Images can be included like this:

In Python: foo.imageurl = "relative_path_to_image" Inside a table cell:

{{ foo.imageurl|image }}

The image will then be scaled to the width of the current table cell. The OpenDocument code should also scale it proportionally horizontally, but that doesn't work yet in LibreOffice, because of this bug (I guess): https://www.libreoffice.org/bugzilla/show_bug.cgi?id=45884

I updated the test-case. When working correctly, it should show a distored star. Currently it shows an undistorted star, because of the LibreOffice bug.

christopher-ramirez commented 10 years ago

Hello and thanks for this PR.

I plan to include support for images in Secretary and this code could help as a bootstrap. But it is very limited in the sense that it only allows to load images within a relative path. I'm looking for a more general purpose solution which allows you to embed images stored in a file system, a DB, in a network or dynamically generated.

Thus, for now I can't merge this PR. But I will keep it as a reference.

Thanks for your contribution.

timostrunk commented 10 years ago

Thanks for considering it.

The relative_url variable in my code means the relative_url inside the OpenDocument zipfile, i.e. the Pictures/my_included_image.png. This will always be the same, also for db access or network access.

It should already work with arbitrary filesystem paths (also absolute ones), as I used standard python file classes. As to the DB access or network access - that would be quite easy with my code, too. The only change required is a URL handler in the image_filter function.

Pseudocode, not python code in image_filter_function:

if image_url.startswith('http://'):
    load_image_into_memory_with_curl
if image_url.startswith('sql://.....')
    load_image_into_memory_with_sqlalchemy
else:
     #block level access:
     self.files[relative_url] = infile_img.read()

The dynamical generation path would need a few more specifications. What you could do is for example encode a python callback inside the LibreOffice document and then again use the url handler: dynamic://PYTHON_FUNCTION_NAME:VARIABLE

if image_url.startswith('dynamic://'):
    image_data = PYTHON_FUNCTION(VARIABLE)
    self.files[relative_url] = image_data

So, I agree - if you already started your solution, it's the best idea to discard mine. If you however did not start coding your solution yet, consider mine again as the extensions you mentioned are very easy to include and just require the change of a single function; the image filter.

christopher-ramirez commented 10 years ago

A preview of image support is now on development branch. https://github.com/christopher-ramirez/secretary/tree/development

I will be glad to know your comments and suggestions about the intended functionality and API.

timostrunk commented 10 years ago

I ported our template to your new code and it immediately worked. Using placeholder images is more intuitive than my solution, so thank you.

There is one thing still bugging me, both in my code and in your code and that is image scaling. What I would really like is set the image width myself and add the image height proportionally. Currently the code stretches the image a lot, if placeholder size and inserted size don't match up.

I used the rel_width: 50% rel_height: scale parameters in my solution, but I know that there is no OpenDocument Editor, which supports it yet. One option would be to simply determine the correct height in regards to the width in the python code (for example using PIL). I could do that, if you want.

christopher-ramirez commented 10 years ago

Thanks for your comments @timostrunk.

In regards to issue with image scaling, a left open the possibility to automatically make adjustments to the frame in a media loader code. You change the svg:height value in draw:frame and secretary will update the image height in the final document. i.e:

    from secretary import Renderer
    engine = Renderer(media_path='.')

    @engine.media_loader
    def my_media_loader(value, *args, **kwargs):
        # Loads a image from File System and updates final image size.
        media = engine.fs_loader(value, *args, **kwargs)

        # Get height and width of current image
        # (PSEUDO) img_height = pil.image_height_in_inch(media[0])
        # (PSEUDO) img_width = pil.image_width_in_inch(media[0])
        kwargs['draw:frame']['svg:height'] = '%din' % img_height
        kwargs['draw:frame']['svg:width'] = '%din' % img_width

        return media

Other solution will be to create in a media loader a transparent canvas with a size defined by svg:height and svg:width and draw the original image scaled upon it.

Finally, I will not add PIL code to secretary since compiling it is a fucking pain. So I prefer each user implement their own solution through a media loader.