better_figures_and_images.py 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. """
  2. Better Figures & Images
  3. ------------------------
  4. This plugin:
  5. - Adds a style="width: ???px; height: auto;" to each image in the content
  6. - Also adds the width of the contained image to any parent div.figures.
  7. - If RESPONSIVE_IMAGES == True, also adds style="max-width: 100%;"
  8. - Corrects alt text: if alt == image filename, set alt = ''
  9. TODO: Need to add a test.py for this plugin.
  10. """
  11. from __future__ import unicode_literals
  12. from os import path, access, R_OK
  13. import os
  14. from pelican import signals
  15. from bs4 import BeautifulSoup
  16. from PIL import Image
  17. import logging
  18. logger = logging.getLogger(__name__)
  19. def content_object_init(instance):
  20. if instance._content is not None:
  21. content = instance._content
  22. soup = BeautifulSoup(content, 'html.parser')
  23. if 'img' in content:
  24. for img in soup('img'):
  25. logger.debug('Better Fig. PATH: %s', instance.settings['PATH'])
  26. logger.debug('Better Fig. img.src: %s', img['src'])
  27. img_path, img_filename = path.split(img['src'])
  28. logger.debug('Better Fig. img_path: %s', img_path)
  29. logger.debug('Better Fig. img_fname: %s', img_filename)
  30. # Strip off {filename}, |filename| or /static
  31. if img_path.startswith(('{filename}', '|filename|')):
  32. img_path = img_path[10:]
  33. elif img_path.startswith('/static'):
  34. img_path = img_path[7:]
  35. elif img_path.startswith('data:image'):
  36. # Image is encoded in-line (not a file).
  37. continue
  38. else:
  39. logger.warning('Better Fig. Error: img_path should start with either {filename}, |filename| or /static')
  40. # search src path list
  41. # 1. Build the source image filename from PATH
  42. # 2. Build the source image filename from STATIC_PATHS
  43. src = os.path.join(instance.settings['PATH'], img_path, img_filename)
  44. src_candidates = [src]
  45. src_candidates += [os.path.join(instance.settings['PATH'], static_path, img_path, img_filename) for static_path in instance.settings['STATIC_PATHS']]
  46. src_candidates = [f for f in src_candidates if path.isfile(f) and access(f, R_OK)]
  47. if not src_candidates:
  48. logger.error('Better Fig. Error: image not found: %s', src)
  49. logger.debug('Better Fig. Skip src: %s', img_path + '/' + img_filename)
  50. continue
  51. src = src_candidates[0]
  52. logger.debug('Better Fig. src: %s', src)
  53. # Open the source image and query dimensions; build style string
  54. im = Image.open(src)
  55. extra_style = 'width: {}px; height: auto;'.format(im.size[0])
  56. if 'RESPONSIVE_IMAGES' in instance.settings and instance.settings['RESPONSIVE_IMAGES']:
  57. extra_style += ' max-width: 100%;'
  58. if img.get('style'):
  59. img['style'] += extra_style
  60. else:
  61. img['style'] = extra_style
  62. if img['alt'] == img['src']:
  63. img['alt'] = ''
  64. fig = img.find_parent('div', 'figure')
  65. if fig:
  66. if fig.get('style'):
  67. fig['style'] += extra_style
  68. else:
  69. fig['style'] = extra_style
  70. instance._content = soup.decode()
  71. def register():
  72. signals.content_object_init.connect(content_object_init)