1
2# Copyright (c) 2010-2024 openpyxl
3
4
5from io import BytesIO
6from warnings import warn
7
8from openpyxl.xml.functions import fromstring
9from openpyxl.xml.constants import IMAGE_NS
10from openpyxl.packaging.relationship import (
11 get_rel,
12 get_rels_path,
13 get_dependents,
14)
15from openpyxl.drawing.spreadsheet_drawing import SpreadsheetDrawing
16from openpyxl.drawing.image import Image, PILImage
17from openpyxl.chart.chartspace import ChartSpace
18from openpyxl.chart.reader import read_chart
19
20
21def find_images(archive, path):
22 """
23 Given the path to a drawing file extract charts and images
24
25 Ignore errors due to unsupported parts of DrawingML
26 """
27
28 src = archive.read(path)
29 tree = fromstring(src)
30 try:
31 drawing = SpreadsheetDrawing.from_tree(tree)
32 except TypeError:
33 warn("DrawingML support is incomplete and limited to charts and images only. Shapes and drawings will be lost.")
34 return [], []
35
36 rels_path = get_rels_path(path)
37 deps = []
38 if rels_path in archive.namelist():
39 deps = get_dependents(archive, rels_path)
40
41 charts = []
42 for rel in drawing._chart_rels:
43 try:
44 cs = get_rel(archive, deps, rel.id, ChartSpace)
45 except TypeError as e:
46 warn(f"Unable to read chart {rel.id} from {path} {e}")
47 continue
48 chart = read_chart(cs)
49 chart.anchor = rel.anchor
50 charts.append(chart)
51
52 images = []
53 if not PILImage: # Pillow not installed, drop images
54 return charts, images
55
56 for rel in drawing._blip_rels:
57 dep = deps.get(rel.embed)
58 if dep.Type == IMAGE_NS:
59 try:
60 image = Image(BytesIO(archive.read(dep.target)))
61 except OSError:
62 msg = "The image {0} will be removed because it cannot be read".format(dep.target)
63 warn(msg)
64 continue
65 if image.format.upper() == "WMF": # cannot save
66 msg = "{0} image format is not supported so the image is being dropped".format(image.format)
67 warn(msg)
68 continue
69 image.anchor = rel.anchor
70 images.append(image)
71 return charts, images