Skip to content

Commit

Permalink
Merge pull request #59 from Terralego/add_image_from_html_locally
Browse files Browse the repository at this point in the history
Add image from html locally
  • Loading branch information
submarcos authored Oct 2, 2020
2 parents ef5221c + 163e3f6 commit ecb0180
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ CHANGELOG
1.3.5.dev0 (XXXX-XX-XX)
----------------------------

* Fix ODT images from html, add src in Pictures

1.3.4 (2020-09-29)
----------------------------
Expand Down
22 changes: 21 additions & 1 deletion template_engines/backends/odt.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import secrets
from pathlib import Path

from bs4 import BeautifulSoup
from django.template.context import make_context
from django.template.exceptions import TemplateDoesNotExist

from template_engines import settings as app_settings
from template_engines.utils import modify_content_document
from template_engines.utils import get_content_url, get_extension_picture, modify_content_document
from template_engines.utils.odt import add_image_in_odt_template
from . import AbstractTemplate, ZipAbstractEngine

Expand Down Expand Up @@ -170,12 +171,31 @@ def replace_inputs(self, soup):
tag.extract()
return soup

def change_pictures_tag(self, tag, context):
name = secrets.token_hex(15)
response = get_content_url(tag['xlink:href'], "get", {})
if response:
context.setdefault('images', {})
picture = response.content
extension = get_extension_picture(picture)
full_name = '{}.{}'.format(name, extension)
context['images'].update({full_name: picture})
tag['xlink:href'] = 'Pictures/%s' % full_name

def replace_pictures(self, soup, context):
draw_list = soup.find_all("draw:image")
for tag in draw_list:
if 'Pictures' not in tag['xlink:href']:
self.change_pictures_tag(tag, context)
return soup

def render(self, context=None, request=None):
context = make_context(context, request)
rendered = self.template.render(context)
soup = BeautifulSoup(rendered, features='html.parser')
soup = self.clean(soup)
soup = self.replace_inputs(soup)
soup = self.replace_pictures(soup, context)

odt_content = modify_content_document(self.template_path, ['content.xml', 'styles.xml'], soup)
for key, image in context.get('images', {}).items():
Expand Down
21 changes: 3 additions & 18 deletions template_engines/templatetags/odt_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
import re
import secrets

import requests
from bs4 import BeautifulSoup
from django import template
from django.utils.safestring import mark_safe

from template_engines.utils import get_content_url, get_extension_picture
from template_engines.utils.odt import ODT_IMAGE
from .utils import get_extension_picture, parse_tag, resize, get_image_infos_from_uri
from .utils import parse_tag, resize, get_image_infos_from_uri

register = template.Library()

Expand Down Expand Up @@ -144,7 +144,6 @@ def parse_br(soup):
def parse_img(soup):
""" Replace img tags with text:p """
imgs = soup.find_all("img")
# TODO: if src starts with http / https, download file and use local path in odt
for img in imgs:
img.name = 'draw:frame'
src = img.attrs.pop('src')
Expand Down Expand Up @@ -211,7 +210,7 @@ def __init__(self, url, data=None, request=None, max_width=None,
def render(self, context):
url, type_request, max_width, max_height, anchor, data = self.get_value_context(context)
name = secrets.token_hex(15)
response = self.get_content_url(url, type_request or "get", data)
response = get_content_url(url, type_request or "get", data)
if not response:
return ""
width, height = resize(response.content, max_width, max_height, odt=True)
Expand All @@ -232,20 +231,6 @@ def get_value_context(self, context):
final_data = "" if not self.data else self.data.resolve(context)
return final_url, final_request, final_max_width, final_max_height, final_anchor, final_data

def get_content_url(self, url, type_request, data):
try:
response = getattr(requests, type_request.lower())(url, data=data)
except requests.exceptions.ConnectionError:
logger.warning("Connection Error, check the url given")
return
except AttributeError:
logger.warning("Type of request specified not allowed")
return
if response.status_code != 200:
logger.warning("The picture is not accessible (Error: %s)" % response.status_code)
return
return response


@register.tag
def image_url_loader(parser, token):
Expand Down
7 changes: 0 additions & 7 deletions template_engines/templatetags/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,6 @@ def parse_tag(token, parser):
return tag_name, args, kwargs


def get_extension_picture(image):
bimage = BytesIO(image)
with Image.open(bimage) as img_reader:
extension = img_reader.format.lower()
return extension


def get_image_infos_from_uri(uri):
""" get image size and dimensions """
if not uri.lower().startswith('http'):
Expand Down
15 changes: 13 additions & 2 deletions template_engines/tests/test_backends/test_odt.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
from ..fake_app.models import Bidon
from ..fake_app.views import OdtTemplateView
from ..settings import (
DOCX_TEMPLATE_PATH, ODT_TEMPLATE_PATH,
TEMPLATES_PATH
DOCX_TEMPLATE_PATH, IMAGE_PATH,
TEMPLATES_PATH, ODT_TEMPLATE_PATH
)


Expand Down Expand Up @@ -89,6 +89,17 @@ def test_view_works_with_from_html(self):
self.assertEqual(response.status_code, 200)
self.assertTrue(response.content)

@mock.patch('requests.get')
def test_view_works_with_from_html_with_image(self, mocked_get):
mocked_get.return_value.status_code = 200
with open(IMAGE_PATH, 'rb') as image_file:
mocked_get.return_value.content = image_file.read()
OdtTemplateView.template_name = os.path.join(TEMPLATES_PATH, 'html.odt')
obj = Bidon.objects.create(name='<img src="http://images.com/monimage.jpeg">')
response = OdtTemplateView.as_view()(self.request, **{'pk': obj.pk}).render()
self.assertEqual(response.status_code, 200)
self.assertTrue(response.content)

def test_view_works_with_bold_text(self):
OdtTemplateView.template_name = os.path.join(TEMPLATES_PATH, 'works.odt')
obj = Bidon.objects.create(name='Michel <b>Pierre</b>')
Expand Down
27 changes: 27 additions & 0 deletions template_engines/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import io
import logging
import os
import re
import requests
from PIL import Image
from tempfile import NamedTemporaryFile
from zipfile import ZipFile

from bs4 import BeautifulSoup
from django.core.files.storage import default_storage

logger = logging.getLogger(__name__)


def get_rendered_by_xml(xml_paths, soup):
dict_xml = {}
Expand Down Expand Up @@ -61,3 +66,25 @@ def clean_tags(content):
lambda e: bad_content.sub('', e.group(0)),
content,
)


def get_content_url(url, type_request, data):
try:
response = getattr(requests, type_request.lower())(url, data=data)
except requests.exceptions.ConnectionError:
logger.warning("Connection Error, check the url given")
return
except AttributeError:
logger.warning("Type of request specified not allowed")
return
if response.status_code != 200:
logger.warning("The picture is not accessible (Error: %s)" % response.status_code)
return
return response


def get_extension_picture(image):
bimage = io.BytesIO(image)
with Image.open(bimage) as img_reader:
extension = img_reader.format.lower()
return extension

0 comments on commit ecb0180

Please sign in to comment.