varia.website/plugins/better_figures_and_images/better_figures_and_images.py

124 lines
4.8 KiB
Python
Raw Normal View History

2018-02-01 14:46:15 +01:00
"""
Better Figures & Images
------------------------
This plugin:
- Adds a style="width: ???px; height: auto;" to each image in the content
- Also adds the width of the contained image to any parent div.figures.
- If RESPONSIVE_IMAGES == True, also adds style="max-width: 100%;"
- Corrects alt text: if alt == image filename, set alt = ''
TODO: Need to add a test.py for this plugin.
"""
from __future__ import unicode_literals
from os import path, access, R_OK
import os
from pelican import signals
from bs4 import BeautifulSoup
from PIL import Image
import pysvg.parser
import logging
logger = logging.getLogger(__name__)
def content_object_init(instance):
if instance._content is not None:
content = instance._content
soup = BeautifulSoup(content, 'html.parser')
for img in soup(['img', 'object']):
logger.debug('Better Fig. PATH: %s', instance.settings['PATH'])
if img.name == 'img':
logger.debug('Better Fig. img.src: %s', img['src'])
img_path, img_filename = path.split(img['src'])
else:
logger.debug('Better Fig. img.data: %s', img['data'])
img_path, img_filename = path.split(img['data'])
logger.debug('Better Fig. img_path: %s', img_path)
logger.debug('Better Fig. img_fname: %s', img_filename)
# Pelican 3.5+ supports {attach} macro for auto copy, in this use case the content does not exist in output
# due to the fact it has not been copied, hence we take it from the source (same as current document)
if img_filename.startswith('{attach}'):
img_path = os.path.dirname(instance.source_path)
img_filename = img_filename[8:]
src = os.path.join(img_path, img_filename)
else:
# Strip off {filename}, |filename| or /static
if img_path.startswith(('{filename}', '|filename|')):
img_path = img_path[10:]
elif img_path.startswith('/static'):
img_path = img_path[7:]
elif img_path.startswith('data:image'):
# Image is encoded in-line (not a file).
continue
else:
logger.warning('Better Fig. Error: img_path should start with either {filename}, |filename| or /static')
# search src path list
# 1. Build the source image filename from PATH
# 2. Build the source image filename from STATIC_PATHS
# if img_path start with '/', remove it.
img_path = os.path.sep.join([el for el in img_path.split("/") if len(el) > 0])
# style: {filename}/static/foo/bar.png
src = os.path.join(instance.settings['PATH'], img_path, img_filename)
src_candidates = [src]
# style: {filename}../static/foo/bar.png
src_candidates += [os.path.join(instance.settings['PATH'], static_path, img_path, img_filename) for static_path in instance.settings['STATIC_PATHS']]
src_candidates = [f for f in src_candidates if path.isfile(f) and access(f, R_OK)]
if not src_candidates:
logger.error('Better Fig. Error: image not found: %s', src)
logger.debug('Better Fig. Skip src: %s', img_path + '/' + img_filename)
continue
src = src_candidates[0]
logger.debug('Better Fig. src: %s', src)
# Open the source image and query dimensions; build style string
try:
if img.name == 'img':
im = Image.open(src)
extra_style = 'width: {}px; height: auto;'.format(im.size[0])
else:
svg = pysvg.parser.parse(src)
extra_style = 'width: {}px; height: auto;'.format(svg.get_width())
except IOError as e:
logger.debug('Better Fig. Failed to open: %s', src)
extra_style = 'width: 100%; height: auto;'
if 'RESPONSIVE_IMAGES' in instance.settings and instance.settings['RESPONSIVE_IMAGES']:
extra_style += ' max-width: 100%;'
if img.get('style'):
img['style'] += extra_style
else:
img['style'] = extra_style
if img.name == 'img':
if img['alt'] == img['src']:
img['alt'] = ''
fig = img.find_parent('div', 'figure')
if fig:
if fig.get('style'):
fig['style'] += extra_style
else:
fig['style'] = extra_style
instance._content = soup.decode()
def register():
signals.content_object_init.connect(content_object_init)