Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

PdfImagePlugin.py 8.8KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  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. #
  18. ##
  19. # Image plugin for PDF images (output only).
  20. ##
  21. import io
  22. import math
  23. import os
  24. import time
  25. from . import Image, ImageFile, ImageSequence, PdfParser, __version__, features
  26. #
  27. # --------------------------------------------------------------------
  28. # object ids:
  29. # 1. catalogue
  30. # 2. pages
  31. # 3. image
  32. # 4. page
  33. # 5. page contents
  34. def _save_all(im, fp, filename):
  35. _save(im, fp, filename, save_all=True)
  36. ##
  37. # (Internal) Image save plugin for the PDF format.
  38. def _save(im, fp, filename, save_all=False):
  39. is_appending = im.encoderinfo.get("append", False)
  40. if is_appending:
  41. existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="r+b")
  42. else:
  43. existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="w+b")
  44. dpi = im.encoderinfo.get("dpi")
  45. if dpi:
  46. x_resolution = dpi[0]
  47. y_resolution = dpi[1]
  48. else:
  49. x_resolution = y_resolution = im.encoderinfo.get("resolution", 72.0)
  50. info = {
  51. "title": None
  52. if is_appending
  53. else os.path.splitext(os.path.basename(filename))[0],
  54. "author": None,
  55. "subject": None,
  56. "keywords": None,
  57. "creator": None,
  58. "producer": None,
  59. "creationDate": None if is_appending else time.gmtime(),
  60. "modDate": None if is_appending else time.gmtime(),
  61. }
  62. for k, default in info.items():
  63. v = im.encoderinfo.get(k) if k in im.encoderinfo else default
  64. if v:
  65. existing_pdf.info[k[0].upper() + k[1:]] = v
  66. #
  67. # make sure image data is available
  68. im.load()
  69. existing_pdf.start_writing()
  70. existing_pdf.write_header()
  71. existing_pdf.write_comment(f"created by Pillow {__version__} PDF driver")
  72. #
  73. # pages
  74. ims = [im]
  75. if save_all:
  76. append_images = im.encoderinfo.get("append_images", [])
  77. for append_im in append_images:
  78. append_im.encoderinfo = im.encoderinfo.copy()
  79. ims.append(append_im)
  80. number_of_pages = 0
  81. image_refs = []
  82. page_refs = []
  83. contents_refs = []
  84. for im in ims:
  85. im_number_of_pages = 1
  86. if save_all:
  87. try:
  88. im_number_of_pages = im.n_frames
  89. except AttributeError:
  90. # Image format does not have n_frames.
  91. # It is a single frame image
  92. pass
  93. number_of_pages += im_number_of_pages
  94. for i in range(im_number_of_pages):
  95. image_refs.append(existing_pdf.next_object_id(0))
  96. page_refs.append(existing_pdf.next_object_id(0))
  97. contents_refs.append(existing_pdf.next_object_id(0))
  98. existing_pdf.pages.append(page_refs[-1])
  99. #
  100. # catalog and list of pages
  101. existing_pdf.write_catalog()
  102. page_number = 0
  103. for im_sequence in ims:
  104. im_pages = ImageSequence.Iterator(im_sequence) if save_all else [im_sequence]
  105. for im in im_pages:
  106. # FIXME: Should replace ASCIIHexDecode with RunLengthDecode
  107. # (packbits) or LZWDecode (tiff/lzw compression). Note that
  108. # PDF 1.2 also supports Flatedecode (zip compression).
  109. bits = 8
  110. params = None
  111. decode = None
  112. #
  113. # Get image characteristics
  114. width, height = im.size
  115. if im.mode == "1":
  116. if features.check("libtiff"):
  117. filter = "CCITTFaxDecode"
  118. bits = 1
  119. params = PdfParser.PdfArray(
  120. [
  121. PdfParser.PdfDict(
  122. {
  123. "K": -1,
  124. "BlackIs1": True,
  125. "Columns": width,
  126. "Rows": height,
  127. }
  128. )
  129. ]
  130. )
  131. else:
  132. filter = "DCTDecode"
  133. colorspace = PdfParser.PdfName("DeviceGray")
  134. procset = "ImageB" # grayscale
  135. elif im.mode == "L":
  136. filter = "DCTDecode"
  137. # params = f"<< /Predictor 15 /Columns {width-2} >>"
  138. colorspace = PdfParser.PdfName("DeviceGray")
  139. procset = "ImageB" # grayscale
  140. elif im.mode == "P":
  141. filter = "ASCIIHexDecode"
  142. palette = im.getpalette()
  143. colorspace = [
  144. PdfParser.PdfName("Indexed"),
  145. PdfParser.PdfName("DeviceRGB"),
  146. 255,
  147. PdfParser.PdfBinary(palette),
  148. ]
  149. procset = "ImageI" # indexed color
  150. elif im.mode == "RGB":
  151. filter = "DCTDecode"
  152. colorspace = PdfParser.PdfName("DeviceRGB")
  153. procset = "ImageC" # color images
  154. elif im.mode == "RGBA":
  155. filter = "JPXDecode"
  156. colorspace = PdfParser.PdfName("DeviceRGB")
  157. procset = "ImageC" # color images
  158. elif im.mode == "CMYK":
  159. filter = "DCTDecode"
  160. colorspace = PdfParser.PdfName("DeviceCMYK")
  161. procset = "ImageC" # color images
  162. decode = [1, 0, 1, 0, 1, 0, 1, 0]
  163. else:
  164. msg = f"cannot save mode {im.mode}"
  165. raise ValueError(msg)
  166. #
  167. # image
  168. op = io.BytesIO()
  169. if filter == "ASCIIHexDecode":
  170. ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)])
  171. elif filter == "CCITTFaxDecode":
  172. im.save(
  173. op,
  174. "TIFF",
  175. compression="group4",
  176. # use a single strip
  177. strip_size=math.ceil(im.width / 8) * im.height,
  178. )
  179. elif filter == "DCTDecode":
  180. Image.SAVE["JPEG"](im, op, filename)
  181. elif filter == "JPXDecode":
  182. Image.SAVE["JPEG2000"](im, op, filename)
  183. elif filter == "FlateDecode":
  184. ImageFile._save(im, op, [("zip", (0, 0) + im.size, 0, im.mode)])
  185. elif filter == "RunLengthDecode":
  186. ImageFile._save(im, op, [("packbits", (0, 0) + im.size, 0, im.mode)])
  187. else:
  188. msg = f"unsupported PDF filter ({filter})"
  189. raise ValueError(msg)
  190. stream = op.getvalue()
  191. if filter == "CCITTFaxDecode":
  192. stream = stream[8:]
  193. filter = PdfParser.PdfArray([PdfParser.PdfName(filter)])
  194. else:
  195. filter = PdfParser.PdfName(filter)
  196. existing_pdf.write_obj(
  197. image_refs[page_number],
  198. stream=stream,
  199. Type=PdfParser.PdfName("XObject"),
  200. Subtype=PdfParser.PdfName("Image"),
  201. Width=width, # * 72.0 / x_resolution,
  202. Height=height, # * 72.0 / y_resolution,
  203. Filter=filter,
  204. BitsPerComponent=bits,
  205. Decode=decode,
  206. DecodeParms=params,
  207. ColorSpace=colorspace,
  208. )
  209. #
  210. # page
  211. existing_pdf.write_page(
  212. page_refs[page_number],
  213. Resources=PdfParser.PdfDict(
  214. ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)],
  215. XObject=PdfParser.PdfDict(image=image_refs[page_number]),
  216. ),
  217. MediaBox=[
  218. 0,
  219. 0,
  220. width * 72.0 / x_resolution,
  221. height * 72.0 / y_resolution,
  222. ],
  223. Contents=contents_refs[page_number],
  224. )
  225. #
  226. # page contents
  227. page_contents = b"q %f 0 0 %f 0 0 cm /image Do Q\n" % (
  228. width * 72.0 / x_resolution,
  229. height * 72.0 / y_resolution,
  230. )
  231. existing_pdf.write_obj(contents_refs[page_number], stream=page_contents)
  232. page_number += 1
  233. #
  234. # trailer
  235. existing_pdf.write_xref_and_trailer()
  236. if hasattr(fp, "flush"):
  237. fp.flush()
  238. existing_pdf.close()
  239. #
  240. # --------------------------------------------------------------------
  241. Image.register_save("PDF", _save)
  242. Image.register_save_all("PDF", _save_all)
  243. Image.register_extension("PDF", ".pdf")
  244. Image.register_mime("PDF", "application/pdf")