Small maker of thumbnails and spitter-out of HTML for galleries.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
4.5 KiB

  1. from PIL import Image, ImageOps, ImageEnhance, ExifTags
  2. from dominate.tags import *
  3. import dominate
  4. import glob as g, os
  5. import json
  6. import traceback
  7. class GalleryHTML:
  8. """A maker of thumbnails, HTML galleries, and simple JSON indices"""
  9. def __init__(self):
  10. # Set some defaults:
  11. self.output_dir = '.'
  12. self.base_url = ""
  13. self.extensions = ['JPG', 'jpg', 'jpeg', 'png', 'gif', 'bmp']
  14. self.image_list = []
  15. self.x = 200
  16. self.y = 200
  17. self.square = False
  18. self.overwrite = False
  19. def add_images_from(self, source):
  20. # Ensure that thumbnail directory exists:
  21. thumb_directory = self.output_dir + "/Thumbs"
  22. os.makedirs(thumb_directory, exist_ok=True)
  23. for image in sorted(self._get_images(source)):
  24. image_basename = os.path.basename(image)
  25. thumbnail_src = self.base_url + 'Thumbs/' + image_basename
  26. image_href = self.base_url + image_basename
  27. image_size = self._thumbnailize(image, thumb_directory + '/' + image_basename)
  28. self.image_list.append((thumbnail_src, image_href, image_size))
  29. def _get_images(self, source):
  30. """Get a list of images from a source directory or pattern."""
  31. images = []
  32. if os.path.isdir(source):
  33. # Grab things with common file extensions out of dirs:
  34. for extension in self.extensions:
  35. images.extend(g.glob(source + '/*.' + extension))
  36. else:
  37. # Assume a pattern or single filename:
  38. images.extend(g.glob(source))
  39. return images
  40. def images_html(self):
  41. """Return HTML for an image list."""
  42. html_output = ''
  43. for display_image in self.image_list:
  44. (x, y) = display_image[2]
  45. html_output += a(img(src=display_image[0], width=x, height=y),
  46. href=display_image[1]).render() + "\n"
  47. return html_output.strip()
  48. def images_json(self):
  49. """Return JSON for an image list."""
  50. return json.dumps(self.image_list)
  51. def _handle_exif(self, source_image):
  52. """
  53. Handle EXIF rotation data, maybe - per:
  54. https://stackoverflow.com/questions/4228530/pil-thumbnail-is-rotating-my-image
  55. """
  56. # PNG files don't have EXIF data; avoid an exception on _getexif():
  57. if source_image.format == 'PNG':
  58. return source_image
  59. try:
  60. for tag in ExifTags.TAGS.keys():
  61. if ExifTags.TAGS[tag] == 'Orientation':
  62. break
  63. if source_image._getexif():
  64. # We have EXIF metadata to work with:
  65. exif = dict(source_image._getexif().items())
  66. orientation = exif.get(tag, False)
  67. if orientation == 3:
  68. source_image = source_image.rotate(180, expand=True)
  69. elif orientation == 6:
  70. source_image = source_image.rotate(270, expand=True)
  71. elif orientation == 8:
  72. source_image = source_image.rotate(90, expand=True)
  73. except:
  74. traceback.print_exc()
  75. return source_image;
  76. def _thumbnailize(self, image, thumbnail_file):
  77. """Write thumbnails."""
  78. image_size = (self.x, self.y)
  79. if os.path.isfile(thumbnail_file) and (not self.overwrite):
  80. # Skip existing files, unless we've been told to overwrite, but make
  81. # sure we know the correct width and height for them:
  82. with open(thumbnail_file, 'r+b') as f:
  83. with Image.open(f) as existing_thumbnail:
  84. image_size = existing_thumbnail.size
  85. else:
  86. # Write a new thumbnail:
  87. with open(image, 'r+b') as f:
  88. with Image.open(f) as source_image:
  89. source_image = self._handle_exif(source_image);
  90. # thumb = ImageEnhance.Sharpness(ImageOps.fit(source_image, (thumb_x, thumb_y))).enhance(0.15)
  91. if self.square:
  92. # Square things off, then make and save thumbnail
  93. smaller_dim = min(source_image.size)
  94. thumb = ImageOps.fit(source_image, (smaller_dim, smaller_dim))
  95. else:
  96. thumb = source_image
  97. thumb.thumbnail((self.x, self.y))
  98. image_size = thumb.size
  99. thumb.save(thumbnail_file, source_image.format)
  100. return image_size