From 98493e6a9ab955e687ede1f9091a168b37093c1e Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 30 Nov 2023 13:16:50 +0800 Subject: [PATCH] use resolution (DPI) from EXIF first, if not found or invalid, then use info from Pillow image --- src/img2pdf.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/img2pdf.py b/src/img2pdf.py index f89670b..64d863d 100755 --- a/src/img2pdf.py +++ b/src/img2pdf.py @@ -22,7 +22,7 @@ import sys import os import zlib import argparse -from PIL import Image, TiffImagePlugin, GifImagePlugin, ImageCms +from PIL import Image, TiffImagePlugin, GifImagePlugin, ImageCms, ExifTags if hasattr(GifImagePlugin, "LoadingStrategy"): # Pillow 9.0.0 started emitting all frames but the first as RGB instead of @@ -1298,6 +1298,7 @@ class pdfdoc(object): def get_imgmetadata( imgdata, imgformat, default_dpi, colorspace, rawdata=None, rotreq=None ): + if imgformat == ImageFormat.JPEG2000 and rawdata is not None and imgdata is None: # this codepath gets called if the PIL installation is not able to # handle JPEG2000 files @@ -1311,7 +1312,25 @@ def get_imgmetadata( else: imgwidthpx, imgheightpx = imgdata.size - ndpi = imgdata.info.get("dpi") + ndpi = None + # For JPEG images with both EXIF tags and JFIF tags, Pillow seems reading image resolution from JFIF. + # However, "Preview" on Mac and "Photos" on Windows read the resolution from EXIF. + # We try to read the value from EXIF first + exif = imgdata.getexif() + if exif: + exif_res_unit = exif.get(ExifTags.Base.ResolutionUnit) + exif_x_res = exif.get(ExifTags.Base.XResolution) + exif_y_res = exif.get(ExifTags.Base.YResolution) + if exif_x_res and exif_y_res: + if (exif_res_unit == 3): # cm + ndpi = (exif_x_res * 2.54, exif_y_res * 2.54) + else: + ndpi = (exif_x_res, exif_y_res) + + # if no DPI from EXIF, get it from `info` + if ndpi is None: + ndpi = imgdata.info.get("dpi") + if ndpi is None: # the PNG plugin of PIL adds the undocumented "aspect" field instead of # the "dpi" field if the PNG pHYs chunk unit is not set to meters