PdfImagePlugin.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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 os
  23. import time
  24. from . import Image, ImageFile, ImageSequence, PdfParser
  25. # __version__ is deprecated and will be removed in a future version. Use
  26. # PIL.__version__ instead.
  27. __version__ = "0.5"
  28. #
  29. # --------------------------------------------------------------------
  30. # object ids:
  31. # 1. catalogue
  32. # 2. pages
  33. # 3. image
  34. # 4. page
  35. # 5. page contents
  36. def _save_all(im, fp, filename):
  37. _save(im, fp, filename, save_all=True)
  38. ##
  39. # (Internal) Image save plugin for the PDF format.
  40. def _save(im, fp, filename, save_all=False):
  41. is_appending = im.encoderinfo.get("append", False)
  42. if is_appending:
  43. existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="r+b")
  44. else:
  45. existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="w+b")
  46. resolution = im.encoderinfo.get("resolution", 72.0)
  47. info = {
  48. "title": None
  49. if is_appending
  50. else os.path.splitext(os.path.basename(filename))[0],
  51. "author": None,
  52. "subject": None,
  53. "keywords": None,
  54. "creator": None,
  55. "producer": None,
  56. "creationDate": None if is_appending else time.gmtime(),
  57. "modDate": None if is_appending else time.gmtime(),
  58. }
  59. for k, default in info.items():
  60. v = im.encoderinfo.get(k) if k in im.encoderinfo else default
  61. if v:
  62. existing_pdf.info[k[0].upper() + k[1:]] = v
  63. #
  64. # make sure image data is available
  65. im.load()
  66. existing_pdf.start_writing()
  67. existing_pdf.write_header()
  68. existing_pdf.write_comment("created by PIL PDF driver " + __version__)
  69. #
  70. # pages
  71. ims = [im]
  72. if save_all:
  73. append_images = im.encoderinfo.get("append_images", [])
  74. for append_im in append_images:
  75. append_im.encoderinfo = im.encoderinfo.copy()
  76. ims.append(append_im)
  77. numberOfPages = 0
  78. image_refs = []
  79. page_refs = []
  80. contents_refs = []
  81. for im in ims:
  82. im_numberOfPages = 1
  83. if save_all:
  84. try:
  85. im_numberOfPages = im.n_frames
  86. except AttributeError:
  87. # Image format does not have n_frames.
  88. # It is a single frame image
  89. pass
  90. numberOfPages += im_numberOfPages
  91. for i in range(im_numberOfPages):
  92. image_refs.append(existing_pdf.next_object_id(0))
  93. page_refs.append(existing_pdf.next_object_id(0))
  94. contents_refs.append(existing_pdf.next_object_id(0))
  95. existing_pdf.pages.append(page_refs[-1])
  96. #
  97. # catalog and list of pages
  98. existing_pdf.write_catalog()
  99. pageNumber = 0
  100. for imSequence in ims:
  101. im_pages = ImageSequence.Iterator(imSequence) if save_all else [imSequence]
  102. for im in im_pages:
  103. # FIXME: Should replace ASCIIHexDecode with RunLengthDecode
  104. # (packbits) or LZWDecode (tiff/lzw compression). Note that
  105. # PDF 1.2 also supports Flatedecode (zip compression).
  106. bits = 8
  107. params = None
  108. if im.mode == "1":
  109. filter = "ASCIIHexDecode"
  110. colorspace = PdfParser.PdfName("DeviceGray")
  111. procset = "ImageB" # grayscale
  112. bits = 1
  113. elif im.mode == "L":
  114. filter = "DCTDecode"
  115. # params = "<< /Predictor 15 /Columns %d >>" % (width-2)
  116. colorspace = PdfParser.PdfName("DeviceGray")
  117. procset = "ImageB" # grayscale
  118. elif im.mode == "P":
  119. filter = "ASCIIHexDecode"
  120. palette = im.im.getpalette("RGB")
  121. colorspace = [
  122. PdfParser.PdfName("Indexed"),
  123. PdfParser.PdfName("DeviceRGB"),
  124. 255,
  125. PdfParser.PdfBinary(palette),
  126. ]
  127. procset = "ImageI" # indexed color
  128. elif im.mode == "RGB":
  129. filter = "DCTDecode"
  130. colorspace = PdfParser.PdfName("DeviceRGB")
  131. procset = "ImageC" # color images
  132. elif im.mode == "CMYK":
  133. filter = "DCTDecode"
  134. colorspace = PdfParser.PdfName("DeviceCMYK")
  135. procset = "ImageC" # color images
  136. else:
  137. raise ValueError("cannot save mode %s" % im.mode)
  138. #
  139. # image
  140. op = io.BytesIO()
  141. if filter == "ASCIIHexDecode":
  142. if bits == 1:
  143. # FIXME: the hex encoder doesn't support packed 1-bit
  144. # images; do things the hard way...
  145. data = im.tobytes("raw", "1")
  146. im = Image.new("L", im.size)
  147. im.putdata(data)
  148. ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)])
  149. elif filter == "DCTDecode":
  150. Image.SAVE["JPEG"](im, op, filename)
  151. elif filter == "FlateDecode":
  152. ImageFile._save(im, op, [("zip", (0, 0) + im.size, 0, im.mode)])
  153. elif filter == "RunLengthDecode":
  154. ImageFile._save(im, op, [("packbits", (0, 0) + im.size, 0, im.mode)])
  155. else:
  156. raise ValueError("unsupported PDF filter (%s)" % filter)
  157. #
  158. # Get image characteristics
  159. width, height = im.size
  160. existing_pdf.write_obj(
  161. image_refs[pageNumber],
  162. stream=op.getvalue(),
  163. Type=PdfParser.PdfName("XObject"),
  164. Subtype=PdfParser.PdfName("Image"),
  165. Width=width, # * 72.0 / resolution,
  166. Height=height, # * 72.0 / resolution,
  167. Filter=PdfParser.PdfName(filter),
  168. BitsPerComponent=bits,
  169. DecodeParams=params,
  170. ColorSpace=colorspace,
  171. )
  172. #
  173. # page
  174. existing_pdf.write_page(
  175. page_refs[pageNumber],
  176. Resources=PdfParser.PdfDict(
  177. ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)],
  178. XObject=PdfParser.PdfDict(image=image_refs[pageNumber]),
  179. ),
  180. MediaBox=[
  181. 0,
  182. 0,
  183. int(width * 72.0 / resolution),
  184. int(height * 72.0 / resolution),
  185. ],
  186. Contents=contents_refs[pageNumber],
  187. )
  188. #
  189. # page contents
  190. page_contents = PdfParser.make_bytes(
  191. "q %d 0 0 %d 0 0 cm /image Do Q\n"
  192. % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))
  193. )
  194. existing_pdf.write_obj(contents_refs[pageNumber], stream=page_contents)
  195. pageNumber += 1
  196. #
  197. # trailer
  198. existing_pdf.write_xref_and_trailer()
  199. if hasattr(fp, "flush"):
  200. fp.flush()
  201. existing_pdf.close()
  202. #
  203. # --------------------------------------------------------------------
  204. Image.register_save("PDF", _save)
  205. Image.register_save_all("PDF", _save_all)
  206. Image.register_extension("PDF", ".pdf")
  207. Image.register_mime("PDF", "application/pdf")