Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/PIL/PdfImagePlugin.py: 7%
99 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1#
2# The Python Imaging Library.
3# $Id$
4#
5# PDF (Acrobat) file handling
6#
7# History:
8# 1996-07-16 fl Created
9# 1997-01-18 fl Fixed header
10# 2004-02-21 fl Fixes for 1/L/CMYK images, etc.
11# 2004-02-24 fl Fixes for 1 and P images.
12#
13# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved.
14# Copyright (c) 1996-1997 by Fredrik Lundh.
15#
16# See the README file for information on usage and redistribution.
17#
19##
20# Image plugin for PDF images (output only).
21##
23import io
24import os
25import time
27from . import Image, ImageFile, ImageSequence, PdfParser, __version__
29#
30# --------------------------------------------------------------------
32# object ids:
33# 1. catalogue
34# 2. pages
35# 3. image
36# 4. page
37# 5. page contents
40def _save_all(im, fp, filename):
41 _save(im, fp, filename, save_all=True)
44##
45# (Internal) Image save plugin for the PDF format.
48def _save(im, fp, filename, save_all=False):
49 is_appending = im.encoderinfo.get("append", False)
50 if is_appending:
51 existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="r+b")
52 else:
53 existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="w+b")
55 resolution = im.encoderinfo.get("resolution", 72.0)
57 info = {
58 "title": None
59 if is_appending
60 else os.path.splitext(os.path.basename(filename))[0],
61 "author": None,
62 "subject": None,
63 "keywords": None,
64 "creator": None,
65 "producer": None,
66 "creationDate": None if is_appending else time.gmtime(),
67 "modDate": None if is_appending else time.gmtime(),
68 }
69 for k, default in info.items():
70 v = im.encoderinfo.get(k) if k in im.encoderinfo else default
71 if v:
72 existing_pdf.info[k[0].upper() + k[1:]] = v
74 #
75 # make sure image data is available
76 im.load()
78 existing_pdf.start_writing()
79 existing_pdf.write_header()
80 existing_pdf.write_comment(f"created by Pillow {__version__} PDF driver")
82 #
83 # pages
84 ims = [im]
85 if save_all:
86 append_images = im.encoderinfo.get("append_images", [])
87 for append_im in append_images:
88 append_im.encoderinfo = im.encoderinfo.copy()
89 ims.append(append_im)
90 number_of_pages = 0
91 image_refs = []
92 page_refs = []
93 contents_refs = []
94 for im in ims:
95 im_number_of_pages = 1
96 if save_all:
97 try:
98 im_number_of_pages = im.n_frames
99 except AttributeError:
100 # Image format does not have n_frames.
101 # It is a single frame image
102 pass
103 number_of_pages += im_number_of_pages
104 for i in range(im_number_of_pages):
105 image_refs.append(existing_pdf.next_object_id(0))
106 page_refs.append(existing_pdf.next_object_id(0))
107 contents_refs.append(existing_pdf.next_object_id(0))
108 existing_pdf.pages.append(page_refs[-1])
110 #
111 # catalog and list of pages
112 existing_pdf.write_catalog()
114 page_number = 0
115 for im_sequence in ims:
116 im_pages = ImageSequence.Iterator(im_sequence) if save_all else [im_sequence]
117 for im in im_pages:
118 # FIXME: Should replace ASCIIHexDecode with RunLengthDecode
119 # (packbits) or LZWDecode (tiff/lzw compression). Note that
120 # PDF 1.2 also supports Flatedecode (zip compression).
122 bits = 8
123 params = None
124 decode = None
126 if im.mode == "1":
127 filter = "DCTDecode"
128 colorspace = PdfParser.PdfName("DeviceGray")
129 procset = "ImageB" # grayscale
130 elif im.mode == "L":
131 filter = "DCTDecode"
132 # params = f"<< /Predictor 15 /Columns {width-2} >>"
133 colorspace = PdfParser.PdfName("DeviceGray")
134 procset = "ImageB" # grayscale
135 elif im.mode == "P":
136 filter = "ASCIIHexDecode"
137 palette = im.getpalette()
138 colorspace = [
139 PdfParser.PdfName("Indexed"),
140 PdfParser.PdfName("DeviceRGB"),
141 255,
142 PdfParser.PdfBinary(palette),
143 ]
144 procset = "ImageI" # indexed color
145 elif im.mode == "RGB":
146 filter = "DCTDecode"
147 colorspace = PdfParser.PdfName("DeviceRGB")
148 procset = "ImageC" # color images
149 elif im.mode == "CMYK":
150 filter = "DCTDecode"
151 colorspace = PdfParser.PdfName("DeviceCMYK")
152 procset = "ImageC" # color images
153 decode = [1, 0, 1, 0, 1, 0, 1, 0]
154 else:
155 raise ValueError(f"cannot save mode {im.mode}")
157 #
158 # image
160 op = io.BytesIO()
162 if filter == "ASCIIHexDecode":
163 ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)])
164 elif filter == "DCTDecode":
165 Image.SAVE["JPEG"](im, op, filename)
166 elif filter == "FlateDecode":
167 ImageFile._save(im, op, [("zip", (0, 0) + im.size, 0, im.mode)])
168 elif filter == "RunLengthDecode":
169 ImageFile._save(im, op, [("packbits", (0, 0) + im.size, 0, im.mode)])
170 else:
171 raise ValueError(f"unsupported PDF filter ({filter})")
173 #
174 # Get image characteristics
176 width, height = im.size
178 existing_pdf.write_obj(
179 image_refs[page_number],
180 stream=op.getvalue(),
181 Type=PdfParser.PdfName("XObject"),
182 Subtype=PdfParser.PdfName("Image"),
183 Width=width, # * 72.0 / resolution,
184 Height=height, # * 72.0 / resolution,
185 Filter=PdfParser.PdfName(filter),
186 BitsPerComponent=bits,
187 Decode=decode,
188 DecodeParams=params,
189 ColorSpace=colorspace,
190 )
192 #
193 # page
195 existing_pdf.write_page(
196 page_refs[page_number],
197 Resources=PdfParser.PdfDict(
198 ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)],
199 XObject=PdfParser.PdfDict(image=image_refs[page_number]),
200 ),
201 MediaBox=[
202 0,
203 0,
204 width * 72.0 / resolution,
205 height * 72.0 / resolution,
206 ],
207 Contents=contents_refs[page_number],
208 )
210 #
211 # page contents
213 page_contents = b"q %f 0 0 %f 0 0 cm /image Do Q\n" % (
214 width * 72.0 / resolution,
215 height * 72.0 / resolution,
216 )
218 existing_pdf.write_obj(contents_refs[page_number], stream=page_contents)
220 page_number += 1
222 #
223 # trailer
224 existing_pdf.write_xref_and_trailer()
225 if hasattr(fp, "flush"):
226 fp.flush()
227 existing_pdf.close()
230#
231# --------------------------------------------------------------------
234Image.register_save("PDF", _save)
235Image.register_save_all("PDF", _save_all)
237Image.register_extension("PDF", ".pdf")
239Image.register_mime("PDF", "application/pdf")