diff --git a/xlsxwriter/utility.py b/xlsxwriter/utility.py index b21bd4039..7bb43e1df 100644 --- a/xlsxwriter/utility.py +++ b/xlsxwriter/utility.py @@ -5,9 +5,14 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright 2013-2023, John McNamara, jmcnamara@cpan.org # -import re import datetime +import hashlib +import os +import re +from struct import unpack from warnings import warn +from .exceptions import UndefinedImageSize +from .exceptions import UnsupportedImageFormat COL_NAMES = {} @@ -873,3 +878,244 @@ def preserve_whitespace(string): return True else: return False + + +def get_image_properties(filename, image_data): + # Extract dimension information from the image file. + height = 0 + width = 0 + x_dpi = 96 + y_dpi = 96 + + if not image_data: + # Open the image file and read in the data. + fh = open(filename, "rb") + data = fh.read() + else: + # Read the image data from the user supplied byte stream. + data = image_data.getvalue() + + digest = hashlib.sha256(data).hexdigest() + + # Get the image filename without the path. + image_name = os.path.basename(filename) + + # Look for some common image file markers. + marker1 = unpack("3s", data[1:4])[0] + marker2 = unpack(">H", data[:2])[0] + marker3 = unpack("2s", data[:2])[0] + marker4 = unpack("I", data[offset + 0 : offset + 4])[0] + marker = unpack("4s", data[offset + 4 : offset + 8])[0] + + # Read the image dimensions. + if marker == b"IHDR": + width = unpack(">I", data[offset + 8 : offset + 12])[0] + height = unpack(">I", data[offset + 12 : offset + 16])[0] + + # Read the image DPI. + if marker == b"pHYs": + x_density = unpack(">I", data[offset + 8 : offset + 12])[0] + y_density = unpack(">I", data[offset + 12 : offset + 16])[0] + units = unpack("b", data[offset + 16 : offset + 17])[0] + + if units == 1: + x_dpi = x_density * 0.0254 + y_dpi = y_density * 0.0254 + + if marker == b"IEND": + end_marker = True + continue + + offset = offset + length + 12 + + return "png", width, height, x_dpi, y_dpi + + +def _process_jpg(data): + # Extract width and height information from a JPEG file. + offset = 2 + data_length = len(data) + end_marker = False + width = 0 + height = 0 + x_dpi = 96 + y_dpi = 96 + + # Search through the image data to read the JPEG markers. + while not end_marker and offset < data_length: + marker = unpack(">H", data[offset + 0 : offset + 2])[0] + length = unpack(">H", data[offset + 2 : offset + 4])[0] + + # Read the height and width in the 0xFFCn elements (except C4, C8 + # and CC which aren't SOF markers). + if ( + (marker & 0xFFF0) == 0xFFC0 + and marker != 0xFFC4 + and marker != 0xFFC8 + and marker != 0xFFCC + ): + height = unpack(">H", data[offset + 5 : offset + 7])[0] + width = unpack(">H", data[offset + 7 : offset + 9])[0] + + # Read the DPI in the 0xFFE0 element. + if marker == 0xFFE0: + units = unpack("b", data[offset + 11 : offset + 12])[0] + x_density = unpack(">H", data[offset + 12 : offset + 14])[0] + y_density = unpack(">H", data[offset + 14 : offset + 16])[0] + + if units == 1: + x_dpi = x_density + y_dpi = y_density + + if units == 2: + x_dpi = x_density * 2.54 + y_dpi = y_density * 2.54 + + # Workaround for incorrect dpi. + if x_dpi == 1: + x_dpi = 96 + if y_dpi == 1: + y_dpi = 96 + + if marker == 0xFFDA: + end_marker = True + continue + + offset = offset + length + 2 + + return "jpeg", width, height, x_dpi, y_dpi + + +def _process_gif(data): + # Extract width and height information from a GIF file. + x_dpi = 96 + y_dpi = 96 + + width = unpack("