pdfdoc.py 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406
  1. #Copyright ReportLab Europe Ltd. 2000-2017
  2. #see license.txt for license details
  3. #history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/pdfbase/pdfdoc.py
  4. __version__='3.4.1'
  5. __doc__="""
  6. The module pdfdoc.py handles the 'outer structure' of PDF documents, ensuring that
  7. all objects are properly cross-referenced and indexed to the nearest byte. The
  8. 'inner structure' - the page descriptions - are presumed to be generated before
  9. each page is saved.
  10. pdfgen.py calls this and provides a 'canvas' object to handle page marking operators.
  11. piddlePDF calls pdfgen and offers a high-level interface.
  12. The classes within this generally mirror structures in the PDF file
  13. and are not part of any public interface. Instead, canvas and font
  14. classes are made available elsewhere for users to manipulate.
  15. """
  16. import types, binascii, codecs, time
  17. from collections import OrderedDict
  18. from reportlab.pdfbase import pdfutils
  19. from reportlab import rl_config
  20. from reportlab.lib.utils import import_zlib, open_for_read, makeFileName, isSeq, isBytes, isUnicode, _digester, isStr, bytestr, annotateException, TimeStamp
  21. from reportlab.lib.rl_accel import escapePDF, fp_str, asciiBase85Encode, asciiBase85Decode
  22. from reportlab.pdfbase import pdfmetrics
  23. from hashlib import md5
  24. from sys import platform
  25. from sys import version_info
  26. from sys import stderr
  27. if platform[:4] == 'java' and version_info[:2] == (2, 1):
  28. # workaround for list()-bug in Jython 2.1 (should be fixed in 2.2)
  29. def list(sequence):
  30. def f(x):
  31. return x
  32. return list(map(f, sequence))
  33. class PDFError(Exception):
  34. pass
  35. # __InternalName__ is a special attribute that can only be set by the Document arbitrator
  36. __InternalName__ = "__InternalName__"
  37. # __RefOnly__ marks reference only elements that must be formatted on top level
  38. __RefOnly__ = "__RefOnly__"
  39. # __Comment__ provides a (one line) comment to inline with an object ref, if present
  40. # if it is more than one line then percentize it...
  41. __Comment__ = "__Comment__"
  42. # name for standard font dictionary
  43. BasicFonts = "BasicFonts"
  44. # name for the pages object
  45. Pages = "Pages"
  46. PDF_VERSION_DEFAULT = (1, 3)
  47. PDF_SUPPORT_VERSION = dict( #map keyword to min version that supports it
  48. transparency = (1, 4),
  49. )
  50. def pdfdocEnc(x):
  51. return x.encode('extpdfdoc') if isinstance(x,str) else x
  52. def format(element, document, toplevel=0):
  53. """Indirection step for formatting.
  54. Ensures that document parameters alter behaviour
  55. of formatting for all elements.
  56. """
  57. if isinstance(element,PDFObject):
  58. if not toplevel and hasattr(element, __RefOnly__):
  59. # the object cannot be a component at non top level.
  60. # make a reference to it and return it's format
  61. return document.Reference(element).format(document)
  62. else:
  63. f = element.format(document)
  64. if not rl_config.invariant and rl_config.pdfComments and hasattr(element, __Comment__):
  65. f = pdfdocEnc("%% %s\n" % element.__Comment__)+f
  66. return f
  67. elif type(element) in (float, int):
  68. #use a controlled number formatting routine
  69. #instead of str, so Jython/Python etc do not differ
  70. return pdfdocEnc(fp_str(element))
  71. elif isBytes(element):
  72. return element
  73. elif isUnicode(element):
  74. return pdfdocEnc(element)
  75. else:
  76. return pdfdocEnc(str(element))
  77. def xObjectName(externalname):
  78. return "FormXob.%s" % externalname
  79. # backwards compatibility
  80. formName = xObjectName
  81. # no encryption
  82. class NoEncryption:
  83. def encode(self, t):
  84. "encode a string, stream, text"
  85. return t
  86. def prepare(self, document):
  87. # get ready to do encryption
  88. pass
  89. def register(self, objnum, version):
  90. # enter a new direct object
  91. pass
  92. def info(self):
  93. # the representation of self in file if any (should be None or PDFDict)
  94. return None
  95. class PDFObject(object):
  96. pass
  97. class DummyDoc(PDFObject):
  98. "used to bypass encryption when required"
  99. encrypt = NoEncryption()
  100. ### the global document structure manager
  101. class PDFDocument(PDFObject):
  102. # set this to define filters
  103. defaultStreamFilters = None
  104. encrypt = NoEncryption() # default no encryption
  105. def __init__(self,
  106. dummyoutline=0,
  107. compression=rl_config.pageCompression,
  108. invariant=rl_config.invariant,
  109. filename=None,
  110. pdfVersion=PDF_VERSION_DEFAULT,
  111. lang=None,
  112. ):
  113. self._ID = None
  114. self.objectcounter = 0
  115. self.shadingCounter = 0
  116. self.inObject = None
  117. self.pageCounter = 1
  118. # allow None value to be passed in to mean 'give system defaults'
  119. if invariant is None:
  120. self.invariant = rl_config.invariant
  121. else:
  122. self.invariant = invariant
  123. self.setCompression(compression)
  124. self._pdfVersion = pdfVersion
  125. # signature for creating PDF ID
  126. sig = self.signature = md5()
  127. sig.update(b"a reportlab document")
  128. self._timeStamp = TimeStamp(self.invariant)
  129. cat = self._timeStamp.t
  130. cat = ascii(cat)
  131. sig.update(bytestr(cat)) # initialize with timestamp digest
  132. # mapping of internal identifier ("Page001") to PDF objectnumber and generation number (34, 0)
  133. self.idToObjectNumberAndVersion = {}
  134. # mapping of internal identifier ("Page001") to PDF object (PDFPage instance)
  135. self.idToObject = {}
  136. # internal id to file location
  137. self.idToOffset = {}
  138. # number to id
  139. self.numberToId = {}
  140. cat = self.Catalog = self._catalog = PDFCatalog()
  141. pages = self.Pages = PDFPages()
  142. cat.Pages = pages
  143. lang = lang if lang else rl_config.documentLang
  144. if lang:
  145. cat.Lang = PDFString(lang)
  146. self.outline = self.Outlines = cat.Outlines = PDFOutlines0() if dummyoutline else PDFOutlines()
  147. self.info = PDFInfo()
  148. #self.Reference(self.Catalog)
  149. #self.Reference(self.Info)
  150. self.fontMapping = {}
  151. #make an empty font dictionary
  152. DD = PDFDictionary({})
  153. DD.__Comment__ = "The standard fonts dictionary"
  154. self.Reference(DD, BasicFonts)
  155. self.delayedFonts = []
  156. def setCompression(self, onoff):
  157. # XXX: maybe this should also set self.defaultStreamFilters?
  158. self.compression = onoff
  159. def ensureMinPdfVersion(self, *keys):
  160. "Ensure that the pdf version is greater than or equal to that specified by the keys"
  161. for k in keys:
  162. self._pdfVersion = max(self._pdfVersion, PDF_SUPPORT_VERSION[k])
  163. def updateSignature(self, thing):
  164. "add information to the signature"
  165. if self._ID: return # but not if its used already!
  166. self.signature.update(bytestr(thing))
  167. def ID(self):
  168. "A unique fingerprint for the file (unless in invariant mode)"
  169. if self._ID:
  170. return self._ID
  171. digest = self.signature.digest()
  172. doc = DummyDoc()
  173. IDs = PDFText(digest,enc='raw').format(doc)
  174. self._ID = (b'\n['+IDs+IDs+b']\n% ReportLab generated PDF document -- digest (http://www.reportlab.com)\n')
  175. return self._ID
  176. def SaveToFile(self, filename, canvas):
  177. if getattr(self,'_savedToFile',False):
  178. raise RuntimeError("class %s instances can only be saved once" % self.__class__.__name__)
  179. self._savedToFile = True
  180. if hasattr(getattr(filename, "write",None),'__call__'):
  181. myfile = 0
  182. f = filename
  183. filename = getattr(f,'name',None)
  184. if isinstance(filename,int):
  185. filename = '<os fd:%d>'% filename
  186. elif not isStr(filename): #try to fix bug reported by Robert Schroll <rschroll at gmail.com>
  187. filename = '<%s@0X%8.8X>' % (f.__class__.__name__,id(f))
  188. filename = makeFileName(filename)
  189. elif isStr(filename):
  190. myfile = 1
  191. filename = makeFileName(filename)
  192. f = open(filename, "wb")
  193. else:
  194. raise TypeError('Cannot use %s as a filename or file' % repr(filename))
  195. data = self.GetPDFData(canvas)
  196. if isUnicode(data):
  197. data = data.encode('latin1')
  198. f.write(data)
  199. if myfile:
  200. f.close()
  201. import os
  202. if os.name=='mac':
  203. from reportlab.lib.utils import markfilename
  204. markfilename(filename) # do platform specific file junk
  205. if getattr(canvas,'_verbosity',None): print('saved %s' % (filename,))
  206. def GetPDFData(self, canvas):
  207. # realize delayed fonts
  208. for fnt in self.delayedFonts:
  209. fnt.addObjects(self)
  210. # add info stuff to signature
  211. self.info.invariant = self.invariant
  212. self.info.digest(self.signature)
  213. ### later: maybe add more info to sig?
  214. # prepare outline
  215. self.Reference(self.Catalog)
  216. self.Reference(self.info)
  217. self.Outlines.prepare(self, canvas)
  218. if self.Outlines.ready<0:
  219. self.Catalog.Outlines = None
  220. return self.format()
  221. def inPage(self):
  222. """specify the current object as a page (enables reference binding and other page features)"""
  223. if self.inObject is not None:
  224. if self.inObject=="page": return
  225. raise ValueError("can't go in page already in object %s" % self.inObject)
  226. self.inObject = "page"
  227. def inForm(self):
  228. """specify that we are in a form xobject (disable page features, etc)"""
  229. # don't need this check anymore since going in a form pushes old context at canvas level.
  230. #if self.inObject not in ["form", None]:
  231. # raise ValueError("can't go in form already in object %s" % self.inObject)
  232. self.inObject = "form"
  233. # don't need to do anything else, I think...
  234. def getInternalFontName(self, psfontname):
  235. fm = self.fontMapping
  236. if psfontname in fm:
  237. return fm[psfontname]
  238. else:
  239. try:
  240. # does pdfmetrics know about it? if so, add
  241. fontObj = pdfmetrics.getFont(psfontname)
  242. if fontObj._dynamicFont:
  243. raise PDFError("getInternalFontName(%s) called for a dynamic font" % repr(psfontname))
  244. fontObj.addObjects(self)
  245. return fm[psfontname]
  246. except KeyError:
  247. raise PDFError("Font %s not known!" % repr(psfontname))
  248. def thisPageName(self):
  249. return "Page"+repr(self.pageCounter)
  250. def thisPageRef(self):
  251. return PDFObjectReference(self.thisPageName())
  252. def addPage(self, page):
  253. name = self.thisPageName()
  254. self.Reference(page, name)
  255. self.Pages.addPage(page)
  256. self.pageCounter += 1
  257. self.inObject = None
  258. def addForm(self, name, form):
  259. """add a Form XObject."""
  260. # XXX should check that name is a legal PDF name
  261. if self.inObject != "form":
  262. self.inForm()
  263. self.Reference(form, xObjectName(name))
  264. self.inObject = None
  265. def annotationName(self, externalname):
  266. return "Annot.%s"%externalname
  267. def addAnnotation(self, name, annotation):
  268. self.Reference(annotation, self.annotationName(name))
  269. def refAnnotation(self, name):
  270. internalname = self.annotationName(name)
  271. return PDFObjectReference(internalname)
  272. def addShading(self, shading):
  273. name = "Sh%d" % self.shadingCounter
  274. self.Reference(shading, name)
  275. self.shadingCounter += 1
  276. return name
  277. def addColor(self,cmyk):
  278. sname = cmyk.spotName
  279. if not sname:
  280. if cmyk.cyan==0 and cmyk.magenta==0 and cmyk.yellow==0:
  281. sname = 'BLACK'
  282. elif cmyk.black==0 and cmyk.magenta==0 and cmyk.yellow==0:
  283. sname = 'CYAN'
  284. elif cmyk.cyan==0 and cmyk.black==0 and cmyk.yellow==0:
  285. sname = 'MAGENTA'
  286. elif cmyk.cyan==0 and cmyk.magenta==0 and cmyk.black==0:
  287. sname = 'YELLOW'
  288. if not sname:
  289. raise ValueError("CMYK colour %r used without a spotName" % cmyk)
  290. else:
  291. cmyk = cmyk.clone(spotName = sname)
  292. name = PDFName(sname)[1:]
  293. if name not in self.idToObject:
  294. sep = PDFSeparationCMYKColor(cmyk).value() #PDFArray([/Separation /name /DeviceCMYK tint_tf])
  295. self.Reference(sep,name)
  296. return name,sname
  297. def setTitle(self, title):
  298. "embeds in PDF file"
  299. if title is None:
  300. self.info.title = '(anonymous)'
  301. else:
  302. self.info.title = title
  303. def setAuthor(self, author):
  304. "embedded in PDF file"
  305. #allow resetting to clear it
  306. if author is None:
  307. self.info.author = '(anonymous)'
  308. else:
  309. self.info.author = author
  310. def setSubject(self, subject):
  311. "embeds in PDF file"
  312. #allow resetting to clear it
  313. if subject is None:
  314. self.info.subject = '(unspecified)'
  315. else:
  316. self.info.subject = subject
  317. def setCreator(self, creator):
  318. "embeds in PDF file"
  319. if creator is None: #allow resetting to clear it
  320. self.info.creator = '(unspecified)'
  321. else:
  322. self.info.creator = creator
  323. def setProducer(self, producer):
  324. "embeds in PDF file"
  325. if producer is None:
  326. self.info.producer = _default_producer
  327. else:
  328. self.info.producer = producer
  329. def setKeywords(self, keywords):
  330. "embeds a string containing keywords in PDF file"
  331. #allow resetting to clear it but ensure it's a string
  332. if keywords is None:
  333. self.info.keywords = ''
  334. else:
  335. self.info.keywords = keywords
  336. def setDateFormatter(self, dateFormatter):
  337. self.info._dateFormatter = dateFormatter
  338. def getAvailableFonts(self):
  339. fontnames = list(self.fontMapping.keys())
  340. # the standard 14 are also always available! (even if not initialized yet)
  341. from reportlab.pdfbase import _fontdata
  342. for name in _fontdata.standardFonts:
  343. if name not in fontnames:
  344. fontnames.append(name)
  345. fontnames.sort()
  346. return fontnames
  347. def format(self):
  348. # register the Catalog/INfo and then format the objects one by one until exhausted
  349. # (possible infinite loop if there is a bug that continually makes new objects/refs...)
  350. # Prepare encryption
  351. self.encrypt.prepare(self)
  352. cat = self.Catalog
  353. info = self.info
  354. self.Reference(cat)
  355. self.Reference(info)
  356. # register the encryption dictionary if present
  357. encryptref = None
  358. encryptinfo = self.encrypt.info()
  359. if encryptinfo:
  360. encryptref = self.Reference(encryptinfo)
  361. # make std fonts (this could be made optional
  362. counter = 0 # start at first object (object 1 after preincrement)
  363. ids = [] # the collection of object ids in object number order
  364. numbertoid = self.numberToId
  365. idToNV = self.idToObjectNumberAndVersion
  366. idToOb = self.idToObject
  367. idToOf = self.idToOffset
  368. ### note that new entries may be "appended" DURING FORMATTING
  369. # __accum__ allows objects to know where they are in the file etc etc
  370. self.__accum__ = File = PDFFile(self._pdfVersion) # output collector
  371. while True:
  372. counter += 1 # do next object...
  373. if counter not in numbertoid: break
  374. oid = numbertoid[counter]
  375. #printidToOb
  376. obj = idToOb[oid]
  377. IO = PDFIndirectObject(oid, obj)
  378. # register object number and version
  379. IOf = IO.format(self)
  380. # add a comment to the PDF output
  381. if not rl_config.invariant and rl_config.pdfComments:
  382. try:
  383. classname = obj.__class__.__name__
  384. except:
  385. classname = ascii(obj)
  386. File.add("%% %s: class %s \n" % (ascii(oid), classname[:50]))
  387. offset = File.add(IOf)
  388. idToOf[oid] = offset
  389. ids.append(oid)
  390. del self.__accum__
  391. # sanity checks (must happen AFTER formatting)
  392. lno = len(numbertoid)
  393. if counter-1!=lno:
  394. raise ValueError("counter %s doesn't match number to id dictionary %s" %(counter, lno))
  395. # now add the xref
  396. xref = PDFCrossReferenceTable()
  397. xref.addsection(0, ids)
  398. xreff = xref.format(self)
  399. xrefoffset = File.add(xreff)
  400. # now add the trailer
  401. trailer = PDFTrailer(
  402. startxref = xrefoffset,
  403. Size = lno+1,
  404. Root = self.Reference(cat),
  405. Info = self.Reference(info),
  406. Encrypt = encryptref,
  407. ID = self.ID(),
  408. )
  409. trailerf = trailer.format(self)
  410. File.add(trailerf)
  411. for ds in getattr(self,'_digiSigs',[]):
  412. ds.sign(File)
  413. # return string format for pdf file
  414. return File.format(self)
  415. def hasForm(self, name):
  416. """test for existence of named form"""
  417. internalname = xObjectName(name)
  418. return internalname in self.idToObject
  419. def getFormBBox(self, name, boxType="MediaBox"):
  420. """get the declared bounding box of the form as a list.
  421. If you specify a different PDF box definition (e.g. the
  422. ArtBox) and it has one, that's what you'll get."""
  423. internalname = xObjectName(name)
  424. if internalname in self.idToObject:
  425. theform = self.idToObject[internalname]
  426. if hasattr(theform,'_extra_pageCatcher_info'):
  427. return theform._extra_pageCatcher_info[boxType]
  428. if isinstance(theform, PDFFormXObject):
  429. # internally defined form
  430. return theform.BBoxList()
  431. elif isinstance(theform, PDFStream):
  432. # externally defined form
  433. return list(theform.dictionary.dict[boxType].sequence)
  434. else:
  435. raise ValueError("I don't understand the form instance %s" % repr(name))
  436. def getXObjectName(self, name):
  437. """Lets canvas find out what form is called internally.
  438. Never mind whether it is defined yet or not."""
  439. return xObjectName(name)
  440. def xobjDict(self, formnames):
  441. """construct an xobject dict (for inclusion in a resource dict, usually)
  442. from a list of form names (images not yet supported)"""
  443. D = {}
  444. for name in formnames:
  445. internalname = xObjectName(name)
  446. reference = PDFObjectReference(internalname)
  447. D[internalname] = reference
  448. #print "xobjDict D", D
  449. return PDFDictionary(D)
  450. def Reference(self, obj, name=None):
  451. ### note references may "grow" during the final formatting pass: don't use d.keys()!
  452. # don't make references to other references, or non instances, unless they are named!
  453. iob = isinstance(obj,PDFObject)
  454. idToObject = self.idToObject
  455. if name is None and (not iob or obj.__class__ is PDFObjectReference):
  456. return obj
  457. if hasattr(obj, __InternalName__):
  458. # already registered
  459. intname = obj.__InternalName__
  460. if name is not None and name!=intname:
  461. raise ValueError("attempt to reregister object %s with new name %s" % (
  462. repr(intname), repr(name)))
  463. if intname not in idToObject:
  464. raise ValueError("object of type %s named as %s, but not registered" % (type(obj),ascii(intname)))
  465. return PDFObjectReference(intname)
  466. # otherwise register the new object
  467. objectcounter = self.objectcounter = self.objectcounter+1
  468. if name is None:
  469. name = "R"+repr(objectcounter)
  470. if name in idToObject:
  471. other = idToObject[name]
  472. if other!=obj:
  473. raise ValueError("redefining named object: "+repr(name))
  474. return PDFObjectReference(name)
  475. if iob:
  476. obj.__InternalName__ = name
  477. #print "name", name, "counter", objectcounter
  478. self.idToObjectNumberAndVersion[name] = (objectcounter, 0)
  479. self.numberToId[objectcounter] = name
  480. idToObject[name] = obj
  481. return PDFObjectReference(name)
  482. ### chapter 4 Objects
  483. PDFtrue = "true"
  484. PDFfalse = "false"
  485. PDFnull = "null"
  486. class PDFText(PDFObject):
  487. def __init__(self, t, enc='utf-8'):
  488. self.t = t
  489. self.enc = enc
  490. def format(self, document):
  491. t = self.t
  492. if isUnicode(t):
  493. t = t.encode(self.enc)
  494. result = binascii.hexlify(document.encrypt.encode(t))
  495. return b"<" + result + b">"
  496. def __str__(self):
  497. dummydoc = DummyDoc()
  498. return self.format(dummydoc)
  499. def PDFnumber(n):
  500. return n
  501. import re
  502. _re_cleanparens=re.compile('[^()]')
  503. del re
  504. def _isbalanced(s):
  505. '''test whether a string is balanced in parens'''
  506. s = _re_cleanparens.sub('',s)
  507. n = 0
  508. for c in s:
  509. if c=='(': n+=1
  510. else:
  511. n -= 1
  512. if n<0: return 0
  513. return not n and 1 or 0
  514. def _checkPdfdoc(utext):
  515. '''return true if no Pdfdoc encoding errors'''
  516. try:
  517. utext.encode('pdfdoc')
  518. return 1
  519. except UnicodeEncodeError as e:
  520. return 0
  521. class PDFString(PDFObject):
  522. unicodeEncValid = False
  523. def __init__(self, s, escape=1, enc='auto'):
  524. '''s can be unicode/utf8 or a PDFString
  525. if escape is true then the output will be passed through escape
  526. if enc is raw then bytes will be left alone
  527. if enc is auto we'll try and automatically adapt to utf_16_be/utf_16_le if the
  528. effective string is not entirely in pdfdoc
  529. if self.unicodeEncValid unicode will use the specifed encoding
  530. '''
  531. if isinstance(s,PDFString):
  532. self.s = s.s
  533. self.escape = s.escape
  534. self.enc = s.enc
  535. else:
  536. self.s = s
  537. self.escape = escape
  538. self.enc = enc
  539. def format(self, document):
  540. s = self.s
  541. enc = getattr(self,'enc','auto')
  542. if isBytes(s):
  543. if enc == 'auto':
  544. try:
  545. if s.startswith(codecs.BOM_UTF16_BE):
  546. u = s.decode('utf_16_be')
  547. elif s.startswith(codecs.BOM_UTF16_LE):
  548. u = s.decode('utf_16_le')
  549. else:
  550. u = s.decode('utf8')
  551. if _checkPdfdoc(u):
  552. s = u.encode('pdfdoc')
  553. else:
  554. s = codecs.BOM_UTF16_BE+u.encode('utf_16_be')
  555. except:
  556. try:
  557. s.decode('pdfdoc')
  558. except:
  559. stderr.write('Error in %s' % (repr(s),))
  560. raise
  561. elif isUnicode(s):
  562. if enc == 'auto':
  563. if _checkPdfdoc(s):
  564. s = s.encode('pdfdoc')
  565. else:
  566. s = codecs.BOM_UTF16_BE+s.encode('utf_16_be')
  567. elif self.unicodeEncValid:
  568. s = s.encode(self.enc)
  569. else:
  570. s = codecs.BOM_UTF16_BE+s.encode('utf_16_be')
  571. else:
  572. raise ValueError('PDFString argument must be str/unicode not %s' % type(s))
  573. escape = getattr(self,'escape',1)
  574. if not isinstance(document.encrypt,NoEncryption):
  575. s = document.encrypt.encode(s)
  576. escape = 1
  577. if escape:
  578. try:
  579. es = "(%s)" % escapePDF(s)
  580. except:
  581. raise ValueError("cannot escape %s %s" % (s, repr(s)))
  582. if escape&2:
  583. es = es.replace('\\012','\n')
  584. if escape&4 and _isbalanced(es):
  585. es = es.replace('\\(','(').replace('\\)',')')
  586. return pdfdocEnc(es)
  587. else:
  588. return b'(' + s + b')'
  589. def __str__(self):
  590. return "(%s)" % escapePDF(self.s)
  591. def PDFName(data,lo=chr(0x21),hi=chr(0x7e)):
  592. # might need to change this to class for encryption
  593. # NOTE: RESULT MUST ALWAYS SUPPORT MEANINGFUL COMPARISONS (EQUALITY) AND HASH
  594. # first convert the name
  595. L = list(data)
  596. for i,c in enumerate(L):
  597. if c<lo or c>hi or c in "%()<>{}[]#":
  598. L[i] = "#"+hex(ord(c))[2:] # forget the 0x thing...
  599. return "/"+(''.join(L))
  600. class PDFDictionary(PDFObject):
  601. multiline = True
  602. def __init__(self, dict=None):
  603. """dict should be namestring to value eg "a": 122 NOT pdfname to value NOT "/a":122"""
  604. if dict is None:
  605. self.dict = {}
  606. else:
  607. self.dict = dict.copy()
  608. def __setitem__(self, name, value):
  609. self.dict[name] = value
  610. def __getitem__(self, a):
  611. return self.dict[a]
  612. def __contains__(self,a):
  613. return a in self.dict
  614. def Reference(self, name, document):
  615. self.dict[name] = document.Reference(self.dict[name])
  616. def format(self, document,IND=b'\n '):
  617. dict = self.dict
  618. try:
  619. keys = list(dict.keys())
  620. except:
  621. print(ascii(dict))
  622. raise
  623. if not isinstance(dict,OrderedDict): keys.sort()
  624. L = [(format(PDFName(k),document)+b" "+format(dict[k],document)) for k in keys]
  625. if (self.multiline and rl_config.pdfMultiLine) or self.multiline=='forced':
  626. L = IND.join(L)
  627. else:
  628. # break up every 6 elements anyway
  629. t=L.insert
  630. for i in reversed(range(6, len(L), 6)):
  631. t(i,b'\n ')
  632. L = b" ".join(L)
  633. return b'<<\n'+L+b'\n>>'
  634. def copy(self):
  635. return PDFDictionary(self.dict)
  636. def normalize(self):
  637. #normalize the names to use RL standard ie Name not /Name
  638. D = self.dict
  639. K = [k for k in D.keys() if k.startswith('/')]
  640. for k in K:
  641. D[k[1:]] = D.pop(k)
  642. class checkPDFNames:
  643. def __init__(self,*names):
  644. self.names = list(map(PDFName,names))
  645. def __call__(self,value):
  646. if not value.startswith('/'):
  647. value=PDFName(value)
  648. if value in self.names:
  649. return value
  650. def checkPDFBoolean(value):
  651. if value in ('true','false'): return value
  652. class CheckedPDFDictionary(PDFDictionary):
  653. validate = {}
  654. def __init__(self,dict=None,validate=None):
  655. PDFDictionary.__init__(self,dict)
  656. if validate: self.validate = validate
  657. def __setitem__(self,name,value):
  658. if name not in self.validate:
  659. raise ValueError('invalid key, %r' % name)
  660. cvalue = self.validate[name](value)
  661. if cvalue is None:
  662. raise ValueError('Bad value %r for key %r' % (value,name))
  663. PDFDictionary.__setitem__(self,name,cvalue)
  664. class ViewerPreferencesPDFDictionary(CheckedPDFDictionary):
  665. validate=dict(
  666. HideToolbar=checkPDFBoolean,
  667. HideMenubar=checkPDFBoolean,
  668. HideWindowUI=checkPDFBoolean,
  669. FitWindow=checkPDFBoolean,
  670. CenterWindow=checkPDFBoolean,
  671. DisplayDocTitle=checkPDFBoolean, #contributed by mark Erbaugh
  672. NonFullScreenPageMode=checkPDFNames(*'UseNone UseOutlines UseThumbs UseOC'.split()),
  673. Direction=checkPDFNames(*'L2R R2L'.split()),
  674. ViewArea=checkPDFNames(*'MediaBox CropBox BleedBox TrimBox ArtBox'.split()),
  675. ViewClip=checkPDFNames(*'MediaBox CropBox BleedBox TrimBox ArtBox'.split()),
  676. PrintArea=checkPDFNames(*'MediaBox CropBox BleedBox TrimBox ArtBox'.split()),
  677. PrintClip=checkPDFNames(*'MediaBox CropBox BleedBox TrimBox ArtBox'.split()),
  678. PrintScaling=checkPDFNames(*'None AppDefault'.split()),
  679. Duplex=checkPDFNames(*'Simplex DuplexFlipShortEdge DuplexFlipLongEdge'.split()),
  680. )
  681. # stream filters are objects to support round trip and
  682. # possibly in the future also support parameters
  683. class PDFStreamFilterZCompress:
  684. pdfname = "FlateDecode"
  685. def encode(self, text):
  686. from reportlab.lib.utils import import_zlib
  687. zlib = import_zlib()
  688. if not zlib: raise ImportError("cannot z-compress zlib unavailable")
  689. if isUnicode(text):
  690. text = text.encode('utf8')
  691. return zlib.compress(text)
  692. def decode(self, encoded):
  693. from reportlab.lib.utils import import_zlib
  694. zlib = import_zlib()
  695. if not zlib: raise ImportError("cannot z-decompress zlib unavailable")
  696. return zlib.decompress(encoded)
  697. # need only one of these, unless we implement parameters later
  698. PDFZCompress = PDFStreamFilterZCompress()
  699. class PDFStreamFilterBase85Encode:
  700. pdfname = "ASCII85Decode"
  701. def encode(self, text):
  702. from reportlab.pdfbase.pdfutils import _wrap
  703. text = asciiBase85Encode(text)
  704. if rl_config.wrapA85:
  705. text = _wrap(text)
  706. return text
  707. def decode(self, text):
  708. return asciiBase85Decode(text)
  709. # need only one of these too
  710. PDFBase85Encode = PDFStreamFilterBase85Encode()
  711. class PDFStream(PDFObject):
  712. '''set dictionary elements explicitly stream.dictionary[name]=value'''
  713. ### compression stuff not implemented yet
  714. __RefOnly__ = 1 # must be at top level
  715. def __init__(self, dictionary=None, content=None, filters=None):
  716. if dictionary is None:
  717. dictionary = PDFDictionary()
  718. self.dictionary = dictionary
  719. self.content = content
  720. self.filters = filters
  721. def format(self, document):
  722. dictionary = self.dictionary
  723. # copy it for modification
  724. dictionary = PDFDictionary(dictionary.dict.copy())
  725. content = self.content
  726. filters = self.filters
  727. if self.content is None:
  728. raise ValueError("stream content not set")
  729. if filters is None:
  730. filters = document.defaultStreamFilters
  731. # only apply filters if they haven't been applied elsewhere
  732. if filters is not None and "Filter" not in dictionary.dict:
  733. # apply filters in reverse order listed
  734. rf = list(filters)
  735. rf.reverse()
  736. fnames = []
  737. for f in rf:
  738. #print "*****************content:"; print repr(content[:200])
  739. #print "*****************filter", f.pdfname
  740. content = f.encode(content)
  741. fnames.insert(0, PDFName(f.pdfname))
  742. #print "*****************finally:"; print content[:200]
  743. #print "****** FILTERS", fnames
  744. #stop
  745. dictionary["Filter"] = PDFArray(fnames)
  746. # "stream encoding is done after all filters have been applied"
  747. content = document.encrypt.encode(content)
  748. fc = format(content, document)
  749. dictionary["Length"] = len(content)
  750. fd = format(dictionary, document)
  751. return fd+b'\nstream\n'+fc+b'endstream\n'
  752. def teststream(content=None):
  753. #content = "" # test
  754. if content is None:
  755. content = teststreamcontent
  756. content = content.strip() + '\n'
  757. S = PDFStream(content = content,
  758. filters=rl_config.useA85 and [PDFBase85Encode,PDFZCompress] or [PDFZCompress])
  759. # nothing else needed...
  760. S.__Comment__ = "test stream"
  761. return S
  762. teststreamcontent = """
  763. 1 0 0 1 0 0 cm BT /F9 12 Tf 14.4 TL ET
  764. 1.00 0.00 1.00 rg
  765. n 72.00 72.00 432.00 648.00 re B*
  766. """
  767. class PDFArray(PDFObject):
  768. multiline = True
  769. def __init__(self, sequence):
  770. self.sequence = list(sequence)
  771. def References(self, document):
  772. """make all objects in sequence references"""
  773. self.sequence = list(map(document.Reference, self.sequence))
  774. def format(self, document, IND=b'\n '):
  775. L = [format(e, document) for e in self.sequence]
  776. if (self.multiline and rl_config.pdfMultiLine) or self.multiline=='forced':
  777. L = IND.join(L)
  778. else:
  779. n=len(L)
  780. if n>10:
  781. # break up every 10 elements anyway
  782. t=L.insert
  783. for i in reversed(range(10, n, 10)):
  784. t(i,b'\n ')
  785. L = b' '.join(L)
  786. else:
  787. L = b' '.join(L)
  788. return b'[ ' + L + b' ]'
  789. class PDFArrayCompact(PDFArray):
  790. multiline=False
  791. class PDFIndirectObject(PDFObject):
  792. __RefOnly__ = 1
  793. def __init__(self, name, content):
  794. self.name = name
  795. self.content = content
  796. def format(self, document):
  797. name = self.name
  798. n, v = document.idToObjectNumberAndVersion[name]
  799. # set encryption parameters
  800. document.encrypt.register(n, v)
  801. fcontent = format(self.content, document, toplevel=1) # yes this is at top level
  802. return (pdfdocEnc("%s %s obj\n"%(n,v))
  803. +fcontent+ (b'' if fcontent.endswith(b'\n') else b'\n')
  804. +b'endobj\n')
  805. class PDFObjectReference(PDFObject):
  806. def __init__(self, name):
  807. self.name = name
  808. def format(self, document):
  809. try:
  810. return pdfdocEnc("%s %s R" % document.idToObjectNumberAndVersion[self.name])
  811. except:
  812. raise KeyError("forward reference to %s not resolved upon final formatting" % repr(self.name))
  813. class PDFFile(PDFObject):
  814. ### just accumulates strings: keeps track of current offset
  815. def __init__(self,pdfVersion=PDF_VERSION_DEFAULT):
  816. self.strings = []
  817. self.write = self.strings.append
  818. self.offset = 0
  819. ### chapter 5
  820. # Following Ken Lunde's advice and the PDF spec, this includes
  821. # some high-order bytes. I chose the characters for Tokyo
  822. # in Shift-JIS encoding, as these cannot be mistaken for
  823. # any other encoding, and we'll be able to tell if something
  824. # has run our PDF files through a dodgy Unicode conversion.
  825. self.add((pdfdocEnc("%%PDF-%s.%s" % pdfVersion) +
  826. b'\n%\223\214\213\236 ReportLab Generated PDF document http://www.reportlab.com\n'
  827. ))
  828. def closeOrReset(self):
  829. pass
  830. def add(self, s):
  831. """should be constructed as late as possible, return position where placed"""
  832. s = pdfdocEnc(s)
  833. result = self.offset
  834. self.offset = result+len(s)
  835. self.write(s)
  836. return result
  837. def format(self, document):
  838. return b''.join(self.strings)
  839. class PDFCrossReferenceSubsection(PDFObject):
  840. def __init__(self, firstentrynumber, idsequence):
  841. self.firstentrynumber = firstentrynumber
  842. self.idsequence = idsequence
  843. def format(self, document):
  844. """id sequence should represent contiguous object nums else error. free numbers not supported (yet)"""
  845. firstentrynumber = self.firstentrynumber
  846. idsequence = self.idsequence
  847. entries = list(idsequence)
  848. nentries = len(idsequence)
  849. # special case: object number 0 is always free
  850. taken = {}
  851. if firstentrynumber==0:
  852. taken[0] = "standard free entry"
  853. nentries = nentries+1
  854. entries.insert(0, "0000000000 65535 f ")
  855. idToNV = document.idToObjectNumberAndVersion
  856. idToOffset = document.idToOffset
  857. lastentrynumber = firstentrynumber+nentries-1
  858. for id in idsequence:
  859. (num, version) = idToNV[id]
  860. if num in taken:
  861. raise ValueError("object number collision %s %s %s" % (num, repr(id), repr(taken[id])))
  862. if num>lastentrynumber or num<firstentrynumber:
  863. raise ValueError("object number %s not in range %s..%s" % (num, firstentrynumber, lastentrynumber))
  864. # compute position in list
  865. rnum = num-firstentrynumber
  866. taken[num] = id
  867. offset = idToOffset[id]
  868. entries[num] = '%0.10d %0.5d n ' % (offset, version)
  869. # now add the initial line
  870. firstline = "%s %s" % (firstentrynumber, nentries)
  871. entries.insert(0, firstline)
  872. # make sure it ends with \n
  873. entries.append("")
  874. return pdfdocEnc('\n'.join(entries))
  875. class PDFCrossReferenceTable(PDFObject):
  876. def __init__(self):
  877. self.sections = []
  878. def addsection(self, firstentry, ids):
  879. section = PDFCrossReferenceSubsection(firstentry, ids)
  880. self.sections.append(section)
  881. def format(self, document):
  882. sections = self.sections
  883. if not sections:
  884. raise ValueError("no crossref sections")
  885. L = [b"xref\n"]
  886. for s in self.sections:
  887. fs = format(s, document)
  888. L.append(fs)
  889. return pdfdocEnc(b''.join(L))
  890. class PDFTrailer(PDFObject):
  891. def __init__(self, startxref, Size=None, Prev=None, Root=None, Info=None, ID=None, Encrypt=None):
  892. self.startxref = startxref
  893. if Size is None or Root is None:
  894. raise ValueError("Size and Root keys required")
  895. dict = self.dict = PDFDictionary()
  896. for (n,v) in [("Size", Size), ("Prev", Prev), ("Root", Root),
  897. ("Info", Info), ("ID", ID), ("Encrypt", Encrypt)]:
  898. if v is not None:
  899. dict[n] = v
  900. dict.multiline='forced'
  901. def format(self, document):
  902. fdict = self.dict.format(document,IND=b'\n')
  903. return b''.join([
  904. b'trailer\n',
  905. fdict,
  906. b'\nstartxref\n',
  907. pdfdocEnc(str(self.startxref)),
  908. b'\n%%EOF\n',
  909. ]
  910. )
  911. #### XXXX skipping incremental update,
  912. #### encryption
  913. #### chapter 6, doc structure
  914. class PDFCatalog(PDFObject):
  915. __Comment__ = "Document Root"
  916. __RefOnly__ = 1
  917. # to override, set as attributes
  918. __Defaults__ = {"Type": PDFName("Catalog"),
  919. "PageMode": PDFName("UseNone"),
  920. "Lang": None,
  921. }
  922. __NoDefault__ = """
  923. Dests Outlines Pages Threads AcroForm Names OpenAction PageMode URI
  924. ViewerPreferences PageLabels PageLayout JavaScript StructTreeRoot SpiderInfo""".split()
  925. __Refs__ = __NoDefault__
  926. def format(self, document):
  927. self.check_format(document)
  928. defaults = self.__Defaults__
  929. Refs = self.__Refs__
  930. D = {}
  931. for k,v in defaults.items():
  932. v = getattr(self,k,v)
  933. if v is not None:
  934. D[k] = v
  935. for k in self.__NoDefault__:
  936. v = getattr(self,k,None)
  937. if v is not None:
  938. D[k] = v
  939. # force objects to be references where required
  940. for k in Refs:
  941. if k in D:
  942. #print"k is", k, "value", D[k]
  943. D[k] = document.Reference(D[k])
  944. dict = PDFDictionary(D)
  945. return format(dict, document)
  946. def showOutline(self):
  947. self.setPageMode("UseOutlines")
  948. def showFullScreen(self):
  949. self.setPageMode("FullScreen")
  950. def setPageLayout(self,layout):
  951. if layout:
  952. self.PageLayout = PDFName(layout)
  953. def setPageMode(self,mode):
  954. if mode:
  955. self.PageMode = PDFName(mode)
  956. def check_format(self, document):
  957. """for use in subclasses"""
  958. pass
  959. class PDFPages(PDFCatalog):
  960. """PAGES TREE WITH ONE INTERNAL NODE, FOR "BALANCING" CHANGE IMPLEMENTATION"""
  961. __Comment__ = "page tree"
  962. __RefOnly__ = 1
  963. # note: could implement page attribute inheritance...
  964. __Defaults__ = {"Type": PDFName("Pages"),
  965. }
  966. __NoDefault__ = "Kids Count Parent".split()
  967. __Refs__ = ["Parent"]
  968. def __init__(self):
  969. self.pages = []
  970. def __getitem__(self, item):
  971. return self.pages[item]
  972. def addPage(self, page):
  973. self.pages.append(page)
  974. def check_format(self, document):
  975. # convert all pages to page references
  976. pages = self.pages
  977. kids = PDFArray(pages)
  978. # make sure all pages are references
  979. kids.References(document)
  980. self.Kids = kids
  981. self.Count = len(pages)
  982. class PDFPage(PDFCatalog):
  983. __Comment__ = "Page dictionary"
  984. # all PDF attributes can be set explicitly
  985. # if this flag is set, the "usual" behavior will be suppressed
  986. Override_default_compilation = 0
  987. __RefOnly__ = 1
  988. __Defaults__ = {"Type": PDFName("Page"),
  989. # "Parent": PDFObjectReference(Pages), # no! use document.Pages
  990. }
  991. __NoDefault__ = """Parent
  992. MediaBox Resources Contents CropBox Rotate Thumb Annots B Dur Hid Trans AA
  993. PieceInfo LastModified SeparationInfo ArtBox TrimBox BleedBox ID PZ
  994. Trans""".split()
  995. __Refs__ = """Contents Parent ID""".split()
  996. pagewidth = 595
  997. pageheight = 842
  998. stream = None
  999. hasImages = 0
  1000. compression = 0
  1001. XObjects = None
  1002. _colorsUsed = {}
  1003. _shadingsUsed = {}
  1004. Trans = None
  1005. # transitionstring?
  1006. # xobjects?
  1007. # annotations
  1008. def __init__(self):
  1009. # set all nodefaults to None
  1010. for name in self.__NoDefault__:
  1011. setattr(self, name, None)
  1012. def setCompression(self, onoff):
  1013. self.compression = onoff
  1014. def setStream(self, code):
  1015. if self.Override_default_compilation:
  1016. raise ValueError("overridden! must set stream explicitly")
  1017. if isSeq(code):
  1018. code = '\n'.join(code)+'\n'
  1019. self.stream = code
  1020. def setPageTransition(self, tranDict):
  1021. self.Trans = PDFDictionary(tranDict)
  1022. def check_format(self, document):
  1023. # set up parameters unless usual behaviour is suppressed
  1024. if self.Override_default_compilation:
  1025. return
  1026. self.MediaBox = self.MediaBox or PDFArray(self.Rotate in (90,270) and [0,0,self.pageheight,self.pagewidth] or [0, 0, self.pagewidth, self.pageheight])
  1027. if not self.Annots:
  1028. self.Annots = None
  1029. else:
  1030. #print self.Annots
  1031. #raise ValueError("annotations not reimplemented yet")
  1032. if not isinstance(self.Annots,PDFObject):
  1033. self.Annots = PDFArray(self.Annots)
  1034. if not self.Contents:
  1035. stream = self.stream
  1036. if not stream:
  1037. self.Contents = teststream()
  1038. else:
  1039. S = PDFStream()
  1040. if self.compression:
  1041. S.filters = rl_config.useA85 and [PDFBase85Encode, PDFZCompress] or [PDFZCompress]
  1042. S.content = stream
  1043. S.__Comment__ = "page stream"
  1044. self.Contents = S
  1045. if not self.Resources:
  1046. resources = PDFResourceDictionary()
  1047. # fonts!
  1048. resources.basicFonts()
  1049. if self.hasImages:
  1050. resources.allProcs()
  1051. else:
  1052. resources.basicProcs()
  1053. if self.XObjects:
  1054. #print "XObjects", self.XObjects.dict
  1055. resources.XObject = self.XObjects
  1056. if self.ExtGState:
  1057. resources.ExtGState = self.ExtGState
  1058. resources.setShading(self._shadingUsed)
  1059. resources.setColorSpace(self._colorsUsed)
  1060. self.Resources = resources
  1061. if not self.Parent:
  1062. pages = document.Pages
  1063. self.Parent = document.Reference(pages)
  1064. #this code contributed by Christian Jacobs <cljacobsen@gmail.com>
  1065. class DuplicatePageLabelPage(Exception):
  1066. pass
  1067. class PDFPageLabels(PDFCatalog):
  1068. __comment__ = None
  1069. __RefOnly__ = 0
  1070. __Defaults__ = {}
  1071. __NoDefault__ = ["Nums"]
  1072. __Refs__ = []
  1073. def __init__(self):
  1074. self.labels = []
  1075. def addPageLabel(self, page, label):
  1076. """ Adds a new PDFPageLabel to this catalog.
  1077. The 'page' argument, an integer, is the page number in the PDF document
  1078. with which the 'label' should be associated. Page numbering in the PDF
  1079. starts at zero! Thus, to change the label on the first page, '0' should be
  1080. provided as an argument, and to change the 6th page, '5' should be provided
  1081. as the argument.
  1082. The 'label' argument should be a PDFPageLabel instance, which describes the
  1083. format of the labels starting on page 'page' in the PDF and continuing
  1084. until the next encounter of a PDFPageLabel.
  1085. The order in which labels are added is not important.
  1086. """
  1087. self.labels.append((page, label))
  1088. def format(self, document):
  1089. try:
  1090. self.labels.sort()
  1091. except DuplicatePageLabelPage:
  1092. tmp = sorted([x[0] for x in self.labels])
  1093. annotateException('\n\n!!!!! Duplicate PageLabel seen for pages %r' % list(set([x for x in tmp if tmp.count(x)>1])))
  1094. labels = []
  1095. for page, label in self.labels:
  1096. labels.append(page)
  1097. labels.append(label)
  1098. self.Nums = PDFArray(labels) #PDFArray makes a copy with list()
  1099. return PDFCatalog.format(self, document)
  1100. class PDFPageLabel(PDFCatalog):
  1101. __Comment__ = None
  1102. __RefOnly__ = 0
  1103. __Defaults__ = {}
  1104. __NoDefault__ = "Type S P St".split()
  1105. __convertible__ = 'ARABIC ROMAN_UPPER ROMAN_LOWER LETTERS_UPPER LETTERS_LOWER'
  1106. ARABIC = 'D'
  1107. ROMAN_UPPER = 'R'
  1108. ROMAN_LOWER = 'r'
  1109. LETTERS_UPPER = 'A'
  1110. LETTERS_LOWER = 'a'
  1111. def __init__(self, style=None, start=None, prefix=None):
  1112. """
  1113. A PDFPageLabel changes the style of page numbering as displayed in a PDF
  1114. viewer. PDF page labels have nothing to do with 'physical' page numbers
  1115. printed on a canvas, but instead influence the 'logical' page numbers
  1116. displayed by PDF viewers. However, when using roman numerals (i, ii,
  1117. iii...) or page prefixes for appendecies (A.1, A.2...) on the physical
  1118. pages PDF page labels are necessary to change the logical page numbers
  1119. displayed by the PDF viewer to match up with the physical numbers. A
  1120. PDFPageLabel changes the properties of numbering at the page on which it
  1121. appears (see the class 'PDFPageLabels' for specifying where a PDFPageLabel
  1122. is associated) and all subsequent pages, until a new PDFPageLabel is
  1123. encountered.
  1124. The arguments to this initialiser determine the properties of all
  1125. subsequent page labels. 'style' determines the numberings style, arabic,
  1126. roman, letters; 'start' specifies the starting number; and 'prefix' any
  1127. prefix to be applied to the page numbers. All these arguments can be left
  1128. out or set to None.
  1129. * style:
  1130. - None: No numbering, can be used to display the prefix only.
  1131. - PDFPageLabel.ARABIC: Use arabic numbers: 1, 2, 3, 4...
  1132. - PDFPageLabel.ROMAN_UPPER: Use upper case roman numerals: I, II, III...
  1133. - PDFPageLabel.ROMAN_LOWER: Use lower case roman numerals: i, ii, iii...
  1134. - PDFPageLabel.LETTERS_UPPER: Use upper case letters: A, B, C, D...
  1135. - PDFPageLabel.LETTERS_LOWER: Use lower case letters: a, b, c, d...
  1136. * start:
  1137. - An integer specifying the starting number for this PDFPageLabel. This
  1138. can be used when numbering style changes to reset the page number back
  1139. to one, ie from roman to arabic, or from arabic to appendecies. Can be
  1140. any positive integer or None. I'm not sure what the effect of
  1141. specifying None is, probably that page numbering continues with the
  1142. current sequence, I'd have to check the spec to clarify though.
  1143. * prefix:
  1144. - A string which is prefixed to the page numbers. Can be used to display
  1145. appendecies in the format: A.1, A.2, ..., B.1, B.2, ... where a
  1146. PDFPageLabel is used to set the properties for the first page of each
  1147. appendix to restart the page numbering at one and set the prefix to the
  1148. appropriate letter for current appendix. The prefix can also be used to
  1149. display text only, if the 'style' is set to None. This can be used to
  1150. display strings such as 'Front', 'Back', or 'Cover' for the covers on
  1151. books.
  1152. """
  1153. if style:
  1154. if style.upper() in self.__convertible__: style = getattr(self,style.upper())
  1155. self.S = PDFName(style)
  1156. if start: self.St = PDFnumber(start)
  1157. if prefix: self.P = PDFString(prefix)
  1158. def __lt__(self,oth):
  1159. if rl_config.errorOnDuplicatePageLabelPage:
  1160. raise DuplicatePageLabelPage()
  1161. return False
  1162. #ends code contributed by Christian Jacobs <cljacobsen@gmail.com>
  1163. def testpage(document):
  1164. P = PDFPage()
  1165. P.Contents = teststream()
  1166. pages = document.Pages
  1167. P.Parent = document.Reference(pages)
  1168. P.MediaBox = PDFArray([0, 0, 595, 841])
  1169. resources = PDFResourceDictionary()
  1170. resources.allProcs() # enable all procsets
  1171. resources.basicFonts()
  1172. P.Resources = resources
  1173. pages.addPage(P)
  1174. #### DUMMY OUTLINES IMPLEMENTATION FOR testing
  1175. DUMMYOUTLINE = """
  1176. <<
  1177. /Count
  1178. 0
  1179. /Type
  1180. /Outlines
  1181. >>"""
  1182. class PDFOutlines0(PDFObject):
  1183. __Comment__ = "TEST OUTLINE!"
  1184. text = DUMMYOUTLINE.replace("\n", '\n')
  1185. __RefOnly__ = 1
  1186. def format(self, document):
  1187. return pdfdocEnc(self.text)
  1188. class OutlineEntryObject(PDFObject):
  1189. "an entry in an outline"
  1190. Title = Dest = Parent = Prev = Next = First = Last = Count = None
  1191. def format(self, document):
  1192. D = {}
  1193. D["Title"] = PDFString(self.Title)
  1194. D["Parent"] = self.Parent
  1195. D["Dest"] = self.Dest
  1196. for n in ("Prev", "Next", "First", "Last", "Count"):
  1197. v = getattr(self, n)
  1198. if v is not None:
  1199. D[n] = v
  1200. PD = PDFDictionary(D)
  1201. return PD.format(document)
  1202. class PDFOutlines(PDFObject):
  1203. """
  1204. takes a recursive list of outline destinations like::
  1205. out = PDFOutline1()
  1206. out.setNames(canvas, # requires canvas for name resolution
  1207. "chapter1dest",
  1208. ("chapter2dest",
  1209. ["chapter2section1dest",
  1210. "chapter2section2dest",
  1211. "chapter2conclusiondest"]
  1212. ), # end of chapter2 description
  1213. "chapter3dest",
  1214. ("chapter4dest", ["c4s1", "c4s2"])
  1215. )
  1216. Higher layers may build this structure incrementally. KISS at base level.
  1217. """
  1218. # first attempt, many possible features missing.
  1219. #no init for now
  1220. mydestinations = ready = None
  1221. counter = 0
  1222. currentlevel = -1 # ie, no levels yet
  1223. def __init__(self):
  1224. self.destinationnamestotitles = {}
  1225. self.destinationstotitles = {}
  1226. self.levelstack = []
  1227. self.buildtree = []
  1228. self.closedict = {} # dictionary of "closed" destinations in the outline
  1229. def addOutlineEntry(self, destinationname, level=0, title=None, closed=None):
  1230. """destinationname of None means "close the tree" """
  1231. if destinationname is None and level!=0:
  1232. raise ValueError("close tree must have level of 0")
  1233. if not isinstance(level,int): raise ValueError("level must be integer, got %s" % type(level))
  1234. if level<0: raise ValueError("negative levels not allowed")
  1235. if title is None: title = destinationname
  1236. currentlevel = self.currentlevel
  1237. stack = self.levelstack
  1238. tree = self.buildtree
  1239. # adjust currentlevel and stack to match level
  1240. if level>currentlevel:
  1241. if level>currentlevel+1:
  1242. raise ValueError("can't jump from outline level %s to level %s, need intermediates (destinationname=%r, title=%r)" %(currentlevel, level, destinationname, title))
  1243. level = currentlevel = currentlevel+1
  1244. stack.append([])
  1245. while level<currentlevel:
  1246. # pop off levels to match
  1247. current = stack[-1]
  1248. del stack[-1]
  1249. previous = stack[-1]
  1250. lastinprevious = previous[-1]
  1251. if isinstance(lastinprevious,tuple):
  1252. (name, sectionlist) = lastinprevious
  1253. raise ValueError("cannot reset existing sections: " + repr(lastinprevious))
  1254. else:
  1255. name = lastinprevious
  1256. sectionlist = current
  1257. previous[-1] = (name, sectionlist)
  1258. #sectionlist.append(current)
  1259. currentlevel = currentlevel-1
  1260. if destinationname is None: return
  1261. stack[-1].append(destinationname)
  1262. self.destinationnamestotitles[destinationname] = title
  1263. if closed: self.closedict[destinationname] = 1
  1264. self.currentlevel = level
  1265. def setDestinations(self, destinationtree):
  1266. self.mydestinations = destinationtree
  1267. def format(self, document):
  1268. #this should never be called if None in (self.first,self.last)
  1269. D = {}
  1270. D["Type"] = PDFName("Outlines")
  1271. D["Count"] = self.count
  1272. D["First"] = self.first
  1273. D["Last"] = self.last
  1274. PD = PDFDictionary(D)
  1275. return PD.format(document)
  1276. def setNames(self, canvas, *nametree):
  1277. desttree = self.translateNames(canvas, nametree)
  1278. self.setDestinations(desttree)
  1279. def setNameList(self, canvas, nametree):
  1280. "Explicit list so I don't need to do in the caller"
  1281. desttree = self.translateNames(canvas, nametree)
  1282. self.setDestinations(desttree)
  1283. def translateNames(self, canvas, object):
  1284. "recursively translate tree of names into tree of destinations"
  1285. destinationnamestotitles = self.destinationnamestotitles
  1286. destinationstotitles = self.destinationstotitles
  1287. closedict = self.closedict
  1288. if isStr(object):
  1289. if not isUnicode(object): object = object.decode('utf8')
  1290. destination = canvas._bookmarkReference(object)
  1291. title = object
  1292. if object in destinationnamestotitles:
  1293. title = destinationnamestotitles[object]
  1294. else:
  1295. destinationnamestotitles[title] = title
  1296. destinationstotitles[destination] = title
  1297. if object in closedict:
  1298. closedict[destination] = 1 # mark destination closed
  1299. return {object: canvas._bookmarkReference(object)} # name-->ref
  1300. if isSeq(object):
  1301. L = []
  1302. for o in object:
  1303. L.append(self.translateNames(canvas, o))
  1304. if isinstance(object,tuple):
  1305. return tuple(L)
  1306. return L
  1307. # bug contributed by Benjamin Dumke <reportlab@benjamin-dumke.de>
  1308. raise TypeError("in outline, destination name must be string: got a %s"%type(object))
  1309. def prepare(self, document, canvas):
  1310. """prepare all data structures required for save operation (create related objects)"""
  1311. if self.mydestinations is None:
  1312. if self.levelstack:
  1313. self.addOutlineEntry(None) # close the tree
  1314. destnames = self.levelstack[0]
  1315. #from pprint import pprint; pprint(destnames); stop
  1316. self.mydestinations = self.translateNames(canvas, destnames)
  1317. else:
  1318. self.first = self.last = None
  1319. self.count = 0
  1320. self.ready = -1
  1321. return
  1322. #self.first = document.objectReference("Outline.First")
  1323. #self.last = document.objectReference("Outline.Last")
  1324. # XXXX this needs to be generalized for closed entries!
  1325. self.count = count(self.mydestinations, self.closedict)
  1326. (self.first, self.last) = self.maketree(document, self.mydestinations, toplevel=1)
  1327. self.ready = 1
  1328. def maketree(self, document, destinationtree, Parent=None, toplevel=0):
  1329. if toplevel:
  1330. levelname = "Outline"
  1331. Parent = document.Reference(document.Outlines)
  1332. else:
  1333. self.count += 1
  1334. levelname = "Outline.%s" % self.count
  1335. if Parent is None:
  1336. raise ValueError("non-top level outline elt parent must be specified")
  1337. if not isSeq(destinationtree):
  1338. raise ValueError("destinationtree must be list or tuple, got %s")
  1339. nelts = len(destinationtree)
  1340. lastindex = nelts-1
  1341. lastelt = firstref = lastref = None
  1342. destinationnamestotitles = self.destinationnamestotitles
  1343. closedict = self.closedict
  1344. for index in range(nelts):
  1345. eltobj = OutlineEntryObject()
  1346. eltobj.Parent = Parent
  1347. eltname = "%s.%s" % (levelname, index)
  1348. eltref = document.Reference(eltobj, eltname)
  1349. #document.add(eltname, eltobj)
  1350. if lastelt is not None:
  1351. lastelt.Next = eltref
  1352. eltobj.Prev = lastref
  1353. if firstref is None:
  1354. firstref = eltref
  1355. lastref = eltref
  1356. lastelt = eltobj # advance eltobj
  1357. lastref = eltref
  1358. elt = destinationtree[index]
  1359. if isinstance(elt,dict):
  1360. # simple leaf {name: dest}
  1361. leafdict = elt
  1362. elif isinstance(elt,tuple):
  1363. # leaf with subsections: ({name: ref}, subsections) XXXX should clean up (see count(...))
  1364. try:
  1365. (leafdict, subsections) = elt
  1366. except:
  1367. raise ValueError("destination tree elt tuple should have two elts, got %s" % len(elt))
  1368. eltobj.Count = count(subsections, closedict)
  1369. (eltobj.First, eltobj.Last) = self.maketree(document, subsections, eltref)
  1370. else:
  1371. raise ValueError("destination tree elt should be dict or tuple, got %s" % type(elt))
  1372. try:
  1373. [(Title, Dest)] = list(leafdict.items())
  1374. except:
  1375. raise ValueError("bad outline leaf dictionary, should have one entry "+bytestr(elt))
  1376. eltobj.Title = destinationnamestotitles[Title]
  1377. eltobj.Dest = Dest
  1378. if isinstance(elt,tuple) and Dest in closedict:
  1379. # closed subsection, count should be negative
  1380. eltobj.Count = -eltobj.Count
  1381. return (firstref, lastref)
  1382. def count(tree, closedict=None):
  1383. """utility for outline: recursively count leaves in a tuple/list tree"""
  1384. from operator import add
  1385. if isinstance(tree,tuple):
  1386. # leaf with subsections XXXX should clean up this structural usage
  1387. (leafdict, subsections) = tree
  1388. [(Title, Dest)] = list(leafdict.items())
  1389. if closedict and Dest in closedict:
  1390. return 1 # closed tree element
  1391. if isSeq(tree):
  1392. #return reduce(add, map(count, tree))
  1393. counts = []
  1394. for e in tree:
  1395. counts.append(count(e, closedict))
  1396. return sum(counts) #used to be: return reduce(add, counts)
  1397. return 1
  1398. _default_producer = "ReportLab PDF Library - www.reportlab.com"
  1399. class PDFInfo(PDFObject):
  1400. """PDF documents can have basic information embedded, viewable from
  1401. File | Document Info in Acrobat Reader. If this is wrong, you get
  1402. Postscript errors while printing, even though it does not print."""
  1403. producer = _default_producer
  1404. creator = "ReportLab PDF Library - www.reportlab.com"
  1405. title = "untitled"
  1406. author = "anonymous"
  1407. subject = "unspecified"
  1408. keywords = ""
  1409. _dateFormatter = None
  1410. def __init__(self):
  1411. self.trapped = 'False' #could be 'True' or 'Unknown'
  1412. def digest(self, md5object):
  1413. # add self information to signature
  1414. for x in (self.title, self.author, self.subject, self.keywords):
  1415. md5object.update(bytestr(x))
  1416. def format(self, document):
  1417. D = {}
  1418. D["Title"] = PDFString(self.title)
  1419. D["Author"] = PDFString(self.author)
  1420. D['ModDate'] = D["CreationDate"] = PDFDate(ts=document._timeStamp,dateFormatter=self._dateFormatter)
  1421. D["Producer"] = PDFString(self.producer)
  1422. D["Creator"] = PDFString(self.creator)
  1423. D["Subject"] = PDFString(self.subject)
  1424. D["Keywords"] = PDFString(self.keywords)
  1425. D["Trapped"] = PDFName(self.trapped)
  1426. PD = PDFDictionary(D)
  1427. return PD.format(document)
  1428. def copy(self):
  1429. "shallow copy - useful in pagecatchering"
  1430. thing = self.__klass__()
  1431. for k, v in self.__dict__.items():
  1432. setattr(thing, k, v)
  1433. return thing
  1434. # skipping thumbnails, etc
  1435. class Annotation(PDFObject):
  1436. """superclass for all annotations."""
  1437. defaults = [("Type", PDFName("Annot"),)]
  1438. required = ("Type", "Rect", "Contents", "Subtype")
  1439. permitted = required+(
  1440. "Border", "C", "T", "M", "F", "H", "BS", "AA", "AS", "Popup", "P", "AP")
  1441. def cvtdict(self, d, escape=1):
  1442. """transform dict args from python form to pdf string rep as needed"""
  1443. Rect = d["Rect"]
  1444. if not isStr(Rect):
  1445. d["Rect"] = PDFArray(Rect)
  1446. d["Contents"] = PDFString(d["Contents"],escape)
  1447. return d
  1448. def AnnotationDict(self, **kw):
  1449. if 'escape' in kw:
  1450. escape = kw['escape']
  1451. del kw['escape']
  1452. else:
  1453. escape = 1
  1454. d = {}
  1455. for (name,val) in self.defaults:
  1456. d[name] = val
  1457. d.update(kw)
  1458. for name in self.required:
  1459. if name not in d:
  1460. raise ValueError("keyword argument %s missing" % name)
  1461. d = self.cvtdict(d,escape=escape)
  1462. permitted = self.permitted
  1463. for name in d.keys():
  1464. if name not in permitted:
  1465. raise ValueError("bad annotation dictionary name %s" % name)
  1466. return PDFDictionary(d)
  1467. def Dict(self):
  1468. raise ValueError("DictString undefined for virtual superclass Annotation, must overload")
  1469. # but usually
  1470. #return self.AnnotationDict(self, Rect=(a,b,c,d)) or whatever
  1471. def format(self, document):
  1472. D = self.Dict()
  1473. return D.format(document)
  1474. class TextAnnotation(Annotation):
  1475. permitted = Annotation.permitted + (
  1476. "Open", "Name")
  1477. def __init__(self, Rect, Contents, **kw):
  1478. self.Rect = Rect
  1479. self.Contents = Contents
  1480. self.otherkw = kw
  1481. def Dict(self):
  1482. d = {}
  1483. d.update(self.otherkw)
  1484. d["Rect"] = self.Rect
  1485. d["Contents"] = self.Contents
  1486. d["Subtype"] = "/Text"
  1487. return self.AnnotationDict(**d)
  1488. class FreeTextAnnotation(Annotation):
  1489. permitted = Annotation.permitted + ("DA",)
  1490. def __init__(self, Rect, Contents, DA, **kw):
  1491. self.Rect = Rect
  1492. self.Contents = Contents
  1493. self.DA = DA
  1494. self.otherkw = kw
  1495. def Dict(self):
  1496. d = {}
  1497. d.update(self.otherkw)
  1498. d["Rect"] = self.Rect
  1499. d["Contents"] = self.Contents
  1500. d["DA"] = self.DA
  1501. d["Subtype"] = "/FreeText"
  1502. return self.AnnotationDict(**d)
  1503. class LinkAnnotation(Annotation):
  1504. permitted = Annotation.permitted + (
  1505. "Dest", "A", "PA")
  1506. def __init__(self, Rect, Contents, Destination, Border="[0 0 1]", **kw):
  1507. self.Border = Border
  1508. self.Rect = Rect
  1509. self.Contents = Contents
  1510. self.Destination = Destination
  1511. self.otherkw = kw
  1512. def dummyDictString(self): # old, testing
  1513. return """
  1514. << /Type /Annot /Subtype /Link /Rect [71 717 190 734] /Border [16 16 1]
  1515. /Dest [23 0 R /Fit] >>
  1516. """
  1517. def Dict(self):
  1518. d = {}
  1519. d.update(self.otherkw)
  1520. d["Border"] = self.Border
  1521. d["Rect"] = self.Rect
  1522. d["Contents"] = self.Contents
  1523. d["Subtype"] = "/Link"
  1524. d["Dest"] = self.Destination
  1525. return self.AnnotationDict(**d)
  1526. class HighlightAnnotation(Annotation):
  1527. """
  1528. HighlightAnnotation is an annotation that highlights the selected area.
  1529. Rect is the mouseover area that will show the contents.
  1530. QuadPoints is a list of points to highlight, you can have many groups of
  1531. four QuadPoints to allow highlighting many lines.
  1532. """
  1533. permitted = Annotation.permitted + ("QuadPoints", )
  1534. def __init__(self, Rect, Contents, QuadPoints, Color=[0.83, 0.89, 0.95], **kw):
  1535. self.Rect = Rect
  1536. self.Contents = Contents
  1537. self.otherkw = kw
  1538. self.QuadPoints = QuadPoints
  1539. self.Color = Color
  1540. def cvtdict(self, d, escape=1):
  1541. """transform dict args from python form to pdf string rep as needed"""
  1542. Rect = d["Rect"]
  1543. Quad = d["QuadPoints"]
  1544. Color = d["C"]
  1545. if not isinstance(Rect, str):
  1546. d["Rect"] = PDFArray(Rect).format(d, IND=b" ")
  1547. if not isinstance(Quad, str):
  1548. d["QuadPoints"] = PDFArray(Quad).format(d, IND=b" ")
  1549. if not isinstance(Color, str):
  1550. d["C"] = PDFArray(Color).format(d, IND=b" ")
  1551. d["Contents"] = PDFString(d["Contents"], escape)
  1552. return d
  1553. def Dict(self):
  1554. d = {}
  1555. d.update(self.otherkw)
  1556. d["Rect"] = self.Rect
  1557. d["Contents"] = self.Contents
  1558. d["Subtype"] = "/Highlight"
  1559. d["QuadPoints"] = self.QuadPoints
  1560. d["C"] = self.Color
  1561. return self.AnnotationDict(**d)
  1562. def rect_to_quad(Rect):
  1563. """
  1564. Utility method to convert a Rect to a QuadPoint
  1565. """
  1566. return [Rect[0], Rect[1], Rect[2], Rect[1],
  1567. Rect[0], Rect[3], Rect[2], Rect[3]]
  1568. # skipping names tree
  1569. # skipping actions
  1570. # skipping names trees
  1571. # skipping to chapter 7
  1572. class PDFRectangle(PDFObject):
  1573. def __init__(self, llx, lly, urx, ury):
  1574. self.llx, self.lly, self.ulx, self.ury = llx, lly, urx, ury
  1575. def format(self, document):
  1576. A = PDFArray([self.llx, self.lly, self.ulx, self.ury])
  1577. return format(A, document)
  1578. class PDFDate(PDFObject):
  1579. # gmt offset now suppported properly
  1580. def __init__(self, ts=None, dateFormatter=None):
  1581. if ts is None:
  1582. ts = TimeStamp()
  1583. self._init(ts)
  1584. self.dateFormatter = dateFormatter
  1585. def _init(self,ts):
  1586. self.date = ts.YMDhms
  1587. self.dhh = ts.dhh
  1588. self.dmm = ts.dmm
  1589. def format(self, doc):
  1590. dfmt = self.dateFormatter or (
  1591. lambda yyyy,mm,dd,hh,m,s:
  1592. "D:%04d%02d%02d%02d%02d%02d%+03d'%02d'"
  1593. % (yyyy,mm,dd,hh,m,s,self.dhh,self.dmm))
  1594. return format(PDFString(dfmt(*self.date)), doc)
  1595. class Destination(PDFObject):
  1596. """
  1597. not a PDFObject! This is a placeholder that can delegates
  1598. to a pdf object only after it has been defined by the methods
  1599. below.
  1600. EG a Destination can refer to Appendix A before it has been
  1601. defined, but only if Appendix A is explicitly noted as a destination
  1602. and resolved before the document is generated...
  1603. For example the following sequence causes resolution before doc generation.
  1604. d = Destination()
  1605. d.fit() # or other format defining method call
  1606. d.setPage(p)
  1607. (at present setPageRef is called on generation of the page).
  1608. """
  1609. representation = format = page = None
  1610. def __init__(self,name):
  1611. self.name = name
  1612. self.fmt = self.page = None
  1613. def format(self, document):
  1614. f = self.fmt
  1615. if f is None: raise ValueError("format not resolved, probably missing URL scheme or undefined destination target for '%s'" % self.name)
  1616. p = self.page
  1617. if p is None: raise ValueError("Page not bound, probably missing URL scheme or undefined destination target for '%s'" % self.name)
  1618. f.page = p
  1619. return f.format(document)
  1620. def xyz(self, left, top, zoom): # see pdfspec mar 11 99 pp184+
  1621. self.fmt = PDFDestinationXYZ(None, left, top, zoom)
  1622. def fit(self):
  1623. self.fmt = PDFDestinationFit(None)
  1624. def fitb(self):
  1625. self.fmt = PDFDestinationFitB(None)
  1626. def fith(self, top):
  1627. self.fmt = PDFDestinationFitH(None,top)
  1628. def fitv(self, left):
  1629. self.fmt = PDFDestinationFitV(None, left)
  1630. def fitbh(self, top):
  1631. self.fmt = PDFDestinationFitBH(None, top)
  1632. def fitbv(self, left):
  1633. self.fmt = PDFDestinationFitBV(None, left)
  1634. def fitr(self, left, bottom, right, top):
  1635. self.fmt = PDFDestinationFitR(None, left, bottom, right, top)
  1636. def setPage(self, page):
  1637. self.page = page
  1638. #self.fmt.page = page # may not yet be defined!
  1639. class PDFDestinationXYZ(PDFObject):
  1640. typename = "XYZ"
  1641. def __init__(self, page, left, top, zoom):
  1642. self.page = page
  1643. self.top = top
  1644. self.zoom = zoom
  1645. self.left = left
  1646. def format(self, document):
  1647. pageref = document.Reference(self.page)
  1648. A = PDFArray( [ pageref, PDFName(self.typename), self.left, self.top, self.zoom ] )
  1649. return format(A, document)
  1650. class PDFDestinationFit(PDFObject):
  1651. typename = "Fit"
  1652. def __init__(self, page):
  1653. self.page = page
  1654. def format(self, document):
  1655. pageref = document.Reference(self.page)
  1656. A = PDFArray( [ pageref, PDFName(self.typename) ] )
  1657. return format(A, document)
  1658. class PDFDestinationFitB(PDFDestinationFit):
  1659. typename = "FitB"
  1660. class PDFDestinationFitH(PDFObject):
  1661. typename = "FitH"
  1662. def __init__(self, page, top):
  1663. self.page = page; self.top=top
  1664. def format(self, document):
  1665. pageref = document.Reference(self.page)
  1666. A = PDFArray( [ pageref, PDFName(self.typename), self.top ] )
  1667. return format(A, document)
  1668. class PDFDestinationFitBH(PDFDestinationFitH):
  1669. typename = "FitBH"
  1670. class PDFDestinationFitV(PDFObject):
  1671. typename = "FitV"
  1672. def __init__(self, page, left):
  1673. self.page = page; self.left=left
  1674. def format(self, document):
  1675. pageref = document.Reference(self.page)
  1676. A = PDFArray( [ pageref, PDFName(self.typename), self.left ] )
  1677. return format(A, document)
  1678. class PDFDestinationFitBV(PDFDestinationFitV):
  1679. typename = "FitBV"
  1680. class PDFDestinationFitR(PDFObject):
  1681. typename = "FitR"
  1682. def __init__(self, page, left, bottom, right, top):
  1683. self.page = page; self.left=left; self.bottom=bottom; self.right=right; self.top=top
  1684. def format(self, document):
  1685. pageref = document.Reference(self.page)
  1686. A = PDFArray( [ pageref, PDFName(self.typename), self.left, self.bottom, self.right, self.top] )
  1687. return format(A, document)
  1688. # named destinations need nothing
  1689. # skipping filespecs
  1690. class PDFResourceDictionary(PDFObject):
  1691. """each element *could* be reset to a reference if desired"""
  1692. def __init__(self,**kwds):
  1693. for _ in self.dict_attributes:
  1694. setattr(self,_,kwds.pop(_,{}))
  1695. # define the basicprocs
  1696. self.basicProcs()
  1697. if 'ProcSet' in kwds:
  1698. self.ProcSet= kwds.pop('ProcSet')
  1699. stdprocs = [PDFName(s) for s in "PDF Text ImageB ImageC ImageI".split()]
  1700. dict_attributes = ("ColorSpace", "XObject", "ExtGState", "Font", "Pattern", "Properties", "Shading")
  1701. def allProcs(self):
  1702. # define all standard procsets
  1703. self.ProcSet = self.stdprocs
  1704. def basicProcs(self):
  1705. self.ProcSet = self.stdprocs[:2] # just PDF and Text
  1706. def basicFonts(self):
  1707. self.Font = PDFObjectReference(BasicFonts)
  1708. def setColorSpace(self,colorsUsed):
  1709. for c,s in colorsUsed.items():
  1710. self.ColorSpace[s] = PDFObjectReference(c)
  1711. def setShading(self,shadingUsed):
  1712. for c,s in shadingUsed.items():
  1713. self.Shading[s] = PDFObjectReference(c)
  1714. def format(self, document):
  1715. D = {}
  1716. for dname in self.dict_attributes:
  1717. v = getattr(self, dname)
  1718. if isinstance(v,dict):
  1719. if v:
  1720. dv = PDFDictionary(v)
  1721. D[dname] = dv
  1722. else:
  1723. D[dname] = v
  1724. v = self.ProcSet
  1725. dname = "ProcSet"
  1726. if isSeq(v):
  1727. if v:
  1728. dv = PDFArray(v)
  1729. D[dname] = dv
  1730. else:
  1731. D[dname] = v
  1732. DD = PDFDictionary(D)
  1733. return format(DD, document)
  1734. ##############################################################################
  1735. #
  1736. # Font objects - the PDFDocument.addFont() method knows which of these
  1737. # to construct when given a user-facing Font object
  1738. #
  1739. ##############################################################################
  1740. class PDFType1Font(PDFObject):
  1741. """no init: set attributes explicitly"""
  1742. __RefOnly__ = 1
  1743. # note! /Name appears to be an undocumented attribute....
  1744. name_attributes = "Type Subtype BaseFont Name".split()
  1745. Type = "Font"
  1746. Subtype = "Type1"
  1747. # these attributes are assumed to already be of the right type
  1748. local_attributes = "FirstChar LastChar Widths Encoding ToUnicode FontDescriptor".split()
  1749. def format(self, document):
  1750. D = {}
  1751. for name in self.name_attributes:
  1752. if hasattr(self, name):
  1753. value = getattr(self, name)
  1754. D[name] = PDFName(value)
  1755. for name in self.local_attributes:
  1756. if hasattr(self, name):
  1757. value = getattr(self, name)
  1758. D[name] = value
  1759. #print D
  1760. PD = PDFDictionary(D)
  1761. return PD.format(document)
  1762. ## These attribute listings will be useful in future, even if we
  1763. ## put them elsewhere
  1764. class PDFTrueTypeFont(PDFType1Font):
  1765. Subtype = "TrueType"
  1766. #local_attributes = "FirstChar LastChar Widths Encoding ToUnicode FontDescriptor".split() #same
  1767. ##class PDFMMType1Font(PDFType1Font):
  1768. ## Subtype = "MMType1"
  1769. ##
  1770. ##class PDFType3Font(PDFType1Font):
  1771. ## Subtype = "Type3"
  1772. ## local_attributes = "FirstChar LastChar Widths CharProcs FontBBox FontMatrix Resources Encoding".split()
  1773. ##
  1774. ##class PDFType0Font(PDFType1Font):
  1775. ## Subtype = "Type0"
  1776. ## local_attributes = "DescendantFonts Encoding".split(
  1777. ##
  1778. ##class PDFCIDFontType0(PDFType1Font):
  1779. ## Subtype = "CIDFontType0"
  1780. ## local_attributes = "CIDSystemInfo FontDescriptor DW W DW2 W2 Registry Ordering Supplement".split()
  1781. ##
  1782. ##class PDFCIDFontType0(PDFType1Font):
  1783. ## Subtype = "CIDFontType2"
  1784. ## local_attributes = "BaseFont CIDToGIDMap CIDSystemInfo FontDescriptor DW W DW2 W2".split()
  1785. ##
  1786. ##class PDFEncoding(PDFType1Font):
  1787. ## Type = "Encoding"
  1788. ## name_attributes = "Type BaseEncoding".split()
  1789. ## # these attributes are assumed to already be of the right type
  1790. ## local_attributes = ["Differences"]
  1791. ##
  1792. # UGLY ALERT - this needs turning into something O-O, it was hacked
  1793. # across from the pdfmetrics.Encoding class to avoid circularity
  1794. # skipping CMaps
  1795. class PDFFormXObject(PDFObject):
  1796. # like page requires .info set by some higher level (doc)
  1797. # XXXX any resource used in a form must be propagated up to the page that (recursively) uses
  1798. # the form!! (not implemented yet).
  1799. XObjects = Annots = BBox = Matrix = Contents = stream = Resources = None
  1800. hasImages = 1 # probably should change
  1801. compression = 0
  1802. def __init__(self, lowerx, lowery, upperx, uppery):
  1803. #not done
  1804. self.lowerx = lowerx; self.lowery=lowery; self.upperx=upperx; self.uppery=uppery
  1805. def setStreamList(self, data):
  1806. if isSeq(data):
  1807. data = '\n'.join(data)
  1808. self.stream = pdfdocEnc(data)
  1809. def BBoxList(self):
  1810. "get the declared bounding box for the form as a list"
  1811. if self.BBox:
  1812. return list(self.BBox.sequence)
  1813. else:
  1814. return [self.lowerx, self.lowery, self.upperx, self.uppery]
  1815. def format(self, document):
  1816. self.BBox = self.BBox or PDFArray([self.lowerx, self.lowery, self.upperx, self.uppery])
  1817. self.Matrix = self.Matrix or PDFArray([1, 0, 0, 1, 0, 0])
  1818. if not self.Annots:
  1819. self.Annots = None
  1820. else:
  1821. #these must be transferred to the page when the form is used
  1822. raise ValueError("annotations don't work in PDFFormXObjects yet")
  1823. if not self.Contents:
  1824. stream = self.stream
  1825. if not stream:
  1826. self.Contents = teststream()
  1827. else:
  1828. S = PDFStream()
  1829. S.content = stream
  1830. # need to add filter stuff (?)
  1831. S.__Comment__ = "xobject form stream"
  1832. self.Contents = S
  1833. if not self.Resources:
  1834. resources = PDFResourceDictionary()
  1835. # fonts!
  1836. resources.basicFonts()
  1837. if self.hasImages:
  1838. resources.allProcs()
  1839. else:
  1840. resources.basicProcs()
  1841. if self.XObjects:
  1842. #print "XObjects", self.XObjects.dict
  1843. resources.XObject = self.XObjects
  1844. self.Resources=resources
  1845. if self.compression:
  1846. self.Contents.filters = rl_config.useA85 and [PDFBase85Encode, PDFZCompress] or [PDFZCompress]
  1847. sdict = self.Contents.dictionary
  1848. sdict["Type"] = PDFName("XObject")
  1849. sdict["Subtype"] = PDFName("Form")
  1850. sdict["FormType"] = 1
  1851. sdict["BBox"] = self.BBox
  1852. sdict["Matrix"] = self.Matrix
  1853. sdict["Resources"] = self.Resources
  1854. return self.Contents.format(document)
  1855. class PDFPostScriptXObject(PDFObject):
  1856. "For embedding PD (e.g. tray commands) in PDF"
  1857. def __init__(self, content=None):
  1858. self.content = content
  1859. def format(self, document):
  1860. S = PDFStream()
  1861. S.content = self.content
  1862. S.__Comment__ = "xobject postscript stream"
  1863. sdict = S.dictionary
  1864. sdict["Type"] = PDFName("XObject")
  1865. sdict["Subtype"] = PDFName("PS")
  1866. return S.format(document)
  1867. _mode2CS={'RGB':'DeviceRGB', 'L':'DeviceGray', 'CMYK':'DeviceCMYK'}
  1868. class PDFImageXObject(PDFObject):
  1869. # first attempts at a hard-coded one
  1870. # in the file, Image XObjects are stream objects. We already
  1871. # have a PDFStream object with 3 attributes: dictionary, content
  1872. # and filters. So the job of this thing is to construct the
  1873. # right PDFStream instance and ask it to format itself.
  1874. def __init__(self, name, source=None, mask=None):
  1875. self.name = name
  1876. self.width = 24
  1877. self.height = 23
  1878. self.bitsPerComponent = 1
  1879. self.colorSpace = 'DeviceGray'
  1880. self._filters = rl_config.useA85 and ('ASCII85Decode',) or ()
  1881. self.streamContent = """
  1882. 003B00 002700 002480 0E4940 114920 14B220 3CB650
  1883. 75FE88 17FF8C 175F14 1C07E2 3803C4 703182 F8EDFC
  1884. B2BBC2 BB6F84 31BFC2 18EA3C 0E3E00 07FC00 03F800
  1885. 1E1800 1FF800>
  1886. """
  1887. self.mask = mask
  1888. if source is None:
  1889. pass # use the canned one.
  1890. elif hasattr(source,'jpeg_fh'):
  1891. self.loadImageFromSRC(source) #it is already a PIL Image
  1892. else:
  1893. # it is a filename
  1894. import os
  1895. ext = os.path.splitext(source)[1].lower()
  1896. src = open_for_read(source)
  1897. try:
  1898. if not(ext in ('.jpg', '.jpeg') and self.loadImageFromJPEG(src)):
  1899. if rl_config.useA85:
  1900. self.loadImageFromA85(src)
  1901. else:
  1902. self.loadImageFromRaw(src)
  1903. finally:
  1904. src.close()
  1905. def loadImageFromA85(self,source):
  1906. IMG=[]
  1907. imagedata = pdfutils.makeA85Image(source,IMG=IMG,detectJpeg=True)
  1908. if not imagedata:
  1909. return self.loadImageFromSRC(IMG[0])
  1910. imagedata = [s.strip() for s in imagedata]
  1911. words = imagedata[1].split()
  1912. self.width, self.height = (int(words[1]),int(words[3]))
  1913. self.colorSpace = {'/RGB':'DeviceRGB', '/G':'DeviceGray', '/CMYK':'DeviceCMYK'}[words[7]]
  1914. self.bitsPerComponent = 8
  1915. self._filters = 'ASCII85Decode','FlateDecode' #'A85','Fl'
  1916. if IMG: self._checkTransparency(IMG[0])
  1917. elif self.mask=='auto': self.mask = None
  1918. self.streamContent = ''.join(imagedata[3:-1])
  1919. def loadImageFromJPEG(self,imageFile):
  1920. try:
  1921. try:
  1922. info = pdfutils.readJPEGInfo(imageFile)
  1923. finally:
  1924. imageFile.seek(0) #reset file pointer
  1925. except:
  1926. return False
  1927. self.width, self.height = info[0], info[1]
  1928. self.bitsPerComponent = 8
  1929. if info[2] == 1:
  1930. self.colorSpace = 'DeviceGray'
  1931. elif info[2] == 3:
  1932. self.colorSpace = 'DeviceRGB'
  1933. else: #maybe should generate an error, is this right for CMYK?
  1934. self.colorSpace = 'DeviceCMYK'
  1935. self._dotrans = 1
  1936. self.streamContent = imageFile.read()
  1937. if rl_config.useA85:
  1938. self.streamContent = asciiBase85Encode(self.streamContent)
  1939. self._filters = 'ASCII85Decode','DCTDecode' #'A85','DCT'
  1940. else:
  1941. self._filters = 'DCTDecode', #'DCT'
  1942. self.mask = None
  1943. return True
  1944. def loadImageFromRaw(self,source):
  1945. IMG=[]
  1946. imagedata = pdfutils.makeRawImage(source,IMG=IMG,detectJpeg=True)
  1947. if not imagedata:
  1948. return self.loadImageFromSRC(IMG[0])
  1949. words = imagedata[1].split()
  1950. self.width = int(words[1])
  1951. self.height = int(words[3])
  1952. self.colorSpace = {'/RGB':'DeviceRGB', '/G':'DeviceGray', '/CMYK':'DeviceCMYK'}[words[7]]
  1953. self.bitsPerComponent = 8
  1954. self._filters = 'FlateDecode', #'Fl'
  1955. if IMG: self._checkTransparency(IMG[0])
  1956. elif self.mask=='auto': self.mask = None
  1957. self.streamContent = b''.join(imagedata[3:-1])
  1958. def _checkTransparency(self,im):
  1959. if self.mask=='auto':
  1960. if im._dataA:
  1961. self.mask = None
  1962. self._smask = PDFImageXObject(_digester(im._dataA.getRGBData()),im._dataA,mask=None)
  1963. self._smask._decode = [0,1]
  1964. else:
  1965. tc = im.getTransparent()
  1966. if tc:
  1967. self.mask = (tc[0], tc[0], tc[1], tc[1], tc[2], tc[2])
  1968. else:
  1969. self.mask = None
  1970. elif hasattr(self.mask,'rgb'):
  1971. _ = self.mask.rgb()
  1972. self.mask = _[0],_[0],_[1],_[1],_[2],_[2]
  1973. def loadImageFromSRC(self, im):
  1974. "Extracts the stream, width and height"
  1975. fp = im.jpeg_fh()
  1976. if fp:
  1977. self.loadImageFromJPEG(fp)
  1978. else:
  1979. zlib = import_zlib()
  1980. if not zlib: return
  1981. self.width, self.height = im.getSize()
  1982. raw = im.getRGBData()
  1983. #assert len(raw) == self.width*self.height, "Wrong amount of data for image expected %sx%s=%s got %s" % (self.width,self.height,self.width*self.height,len(raw))
  1984. self.streamContent = zlib.compress(raw)
  1985. if rl_config.useA85:
  1986. self.streamContent = asciiBase85Encode(self.streamContent)
  1987. self._filters = 'ASCII85Decode','FlateDecode' #'A85','Fl'
  1988. else:
  1989. self._filters = 'FlateDecode', #'Fl'
  1990. self.colorSpace= _mode2CS[im.mode]
  1991. self.bitsPerComponent = 8
  1992. self._checkTransparency(im)
  1993. def format(self, document):
  1994. S = PDFStream(content = self.streamContent)
  1995. dict = S.dictionary
  1996. dict["Type"] = PDFName("XObject")
  1997. dict["Subtype"] = PDFName("Image")
  1998. dict["Width"] = self.width
  1999. dict["Height"] = self.height
  2000. dict["BitsPerComponent"] = self.bitsPerComponent
  2001. dict["ColorSpace"] = PDFName(self.colorSpace)
  2002. if self.colorSpace=='DeviceCMYK' and getattr(self,'_dotrans',0):
  2003. dict["Decode"] = PDFArray([1,0,1,0,1,0,1,0])
  2004. elif getattr(self,'_decode',None):
  2005. dict["Decode"] = PDFArray(self._decode)
  2006. dict["Filter"] = PDFArray(map(PDFName,self._filters))
  2007. dict["Length"] = len(self.streamContent)
  2008. if self.mask: dict["Mask"] = PDFArray(self.mask)
  2009. if getattr(self,'smask',None): dict["SMask"] = self.smask
  2010. return S.format(document)
  2011. class PDFSeparationCMYKColor:
  2012. def __init__(self, cmyk):
  2013. from reportlab.lib.colors import CMYKColor
  2014. if not isinstance(cmyk,CMYKColor):
  2015. raise ValueError('%s needs a CMYKColor argument' % self.__class__.__name__)
  2016. elif not cmyk.spotName:
  2017. raise ValueError('%s needs a CMYKColor argument with a spotName' % self.__class__.__name__)
  2018. self.cmyk = cmyk
  2019. def _makeFuncPS(self):
  2020. '''create the postscript code for the tint transfer function
  2021. effectively this is tint*c, tint*y, ... tint*k'''
  2022. R = [].append
  2023. for i,v in enumerate(self.cmyk.cmyk()):
  2024. v=float(v)
  2025. if i==3:
  2026. if v==0.0:
  2027. R('pop')
  2028. R('0.0')
  2029. else:
  2030. R(str(v))
  2031. R('mul')
  2032. else:
  2033. if v==0:
  2034. R('0.0')
  2035. else:
  2036. R('dup')
  2037. R(str(v))
  2038. R('mul')
  2039. R('exch')
  2040. return '{%s}' % (' '.join(R.__self__))
  2041. def value(self):
  2042. return PDFArrayCompact((
  2043. PDFName('Separation'),
  2044. PDFName(self.cmyk.spotName),
  2045. PDFName('DeviceCMYK'),
  2046. PDFStream(
  2047. dictionary=PDFDictionary(dict(
  2048. FunctionType=4,
  2049. Domain=PDFArrayCompact((0,1)),
  2050. Range=PDFArrayCompact((0,1,0,1,0,1,0,1))
  2051. )),
  2052. content=self._makeFuncPS(),
  2053. filters=None,#[PDFBase85Encode, PDFZCompress],
  2054. )
  2055. ))
  2056. class PDFFunction(PDFObject):
  2057. """superclass for all function types."""
  2058. defaults = []
  2059. required = ("FunctionType", "Domain")
  2060. permitted = required+("Range",)
  2061. def FunctionDict(self, **kw):
  2062. d = {}
  2063. for (name,val) in self.defaults:
  2064. d[name] = val
  2065. d.update(kw)
  2066. for name in self.required:
  2067. if name not in d:
  2068. raise ValueError("keyword argument %s missing" % name)
  2069. permitted = self.permitted
  2070. for name in d.keys():
  2071. if name not in permitted:
  2072. raise ValueError("bad annotation dictionary name %s" % name)
  2073. return PDFDictionary(d)
  2074. def Dict(self, document):
  2075. raise ValueError("Dict undefined for virtual superclass PDFShading, must overload")
  2076. # but usually
  2077. #return self.FunctionDict(self, ...)
  2078. def format(self, document):
  2079. D = self.Dict(document)
  2080. return D.format(document)
  2081. class PDFExponentialFunction(PDFFunction):
  2082. defaults = PDFFunction.defaults + [("Domain", PDFArrayCompact((0.0, 1.0)))]
  2083. required = PDFFunction.required + ("N",)
  2084. permitted = PDFFunction.permitted + ("C0", "C1", "N")
  2085. def __init__(self, C0, C1, N, **kw):
  2086. self.C0 = C0
  2087. self.C1 = C1
  2088. self.N = N
  2089. self.otherkw = kw
  2090. def Dict(self, document):
  2091. d = {}
  2092. d.update(self.otherkw)
  2093. d["FunctionType"] = 2
  2094. d["C0"] = PDFArrayCompact(self.C0)
  2095. d["C1"] = PDFArrayCompact(self.C1)
  2096. d["N"] = self.N
  2097. return self.FunctionDict(**d)
  2098. class PDFStitchingFunction(PDFFunction):
  2099. required = PDFFunction.required + ("Functions", "Bounds", "Encode")
  2100. permitted = PDFFunction.permitted + ("Functions", "Bounds", "Encode")
  2101. def __init__(self, Functions, Bounds, Encode, **kw):
  2102. self.Functions = Functions
  2103. self.Bounds = Bounds
  2104. self.Encode = Encode
  2105. self.otherkw = kw
  2106. def Dict(self, document):
  2107. d = {}
  2108. d.update(self.otherkw)
  2109. d["FunctionType"] = 3
  2110. d["Functions"] = PDFArray([document.Reference(x) for x in self.Functions])
  2111. d["Bounds"] = PDFArray(self.Bounds)
  2112. d["Encode"] = PDFArray(self.Encode)
  2113. return self.FunctionDict(**d)
  2114. class PDFShading(PDFObject):
  2115. """superclass for all shading types."""
  2116. required = ("ShadingType", "ColorSpace")
  2117. permitted = required+("Background", "BBox", "AntiAlias")
  2118. def ShadingDict(self, **kw):
  2119. d = {}
  2120. d.update(kw)
  2121. for name in self.required:
  2122. if name not in d:
  2123. raise ValueError("keyword argument %s missing" % name)
  2124. permitted = self.permitted
  2125. for name in d.keys():
  2126. if name not in permitted:
  2127. raise ValueError("bad annotation dictionary name %s" % name)
  2128. return PDFDictionary(d)
  2129. def Dict(self, document):
  2130. raise ValueError("Dict undefined for virtual superclass PDFShading, must overload")
  2131. # but usually
  2132. #return self.ShadingDict(self, ...)
  2133. def format(self, document):
  2134. D = self.Dict(document)
  2135. return D.format(document)
  2136. class PDFFunctionShading(PDFShading):
  2137. required = PDFShading.required + ("Function",)
  2138. permitted = PDFShading.permitted + ("Domain", "Matrix", "Function")
  2139. def __init__(self, Function, ColorSpace, **kw):
  2140. self.Function = Function
  2141. self.ColorSpace = ColorSpace
  2142. self.otherkw = kw
  2143. def Dict(self, document):
  2144. d = {}
  2145. d.update(self.otherkw)
  2146. d["ShadingType"] = 1
  2147. d["ColorSpace"] = PDFName(self.ColorSpace)
  2148. d["Function"] = document.Reference(self.Function)
  2149. return self.ShadingDict(**d)
  2150. class PDFAxialShading(PDFShading):
  2151. required = PDFShading.required + ("Coords", "Function")
  2152. permitted = PDFShading.permitted + (
  2153. "Coords", "Domain", "Function", "Extend")
  2154. def __init__(self, x0, y0, x1, y1, Function, ColorSpace, **kw):
  2155. self.Coords = (x0, y0, x1, y1)
  2156. self.Function = Function
  2157. self.ColorSpace = ColorSpace
  2158. self.otherkw = kw
  2159. def Dict(self, document):
  2160. d = {}
  2161. d.update(self.otherkw)
  2162. d["ShadingType"] = 2
  2163. d["ColorSpace"] = PDFName(self.ColorSpace)
  2164. d["Coords"] = PDFArrayCompact(self.Coords)
  2165. d["Function"] = document.Reference(self.Function)
  2166. return self.ShadingDict(**d)
  2167. class PDFRadialShading(PDFShading):
  2168. required = PDFShading.required + ("Coords", "Function")
  2169. permitted = PDFShading.permitted + (
  2170. "Coords", "Domain", "Function", "Extend")
  2171. def __init__(self, x0, y0, r0, x1, y1, r1, Function, ColorSpace, **kw):
  2172. self.Coords = (x0, y0, r0, x1, y1, r1)
  2173. self.Function = Function
  2174. self.ColorSpace = ColorSpace
  2175. self.otherkw = kw
  2176. def Dict(self, document):
  2177. d = {}
  2178. d.update(self.otherkw)
  2179. d["ShadingType"] = 3
  2180. d["ColorSpace"] = PDFName(self.ColorSpace)
  2181. d["Coords"] = PDFArrayCompact(self.Coords)
  2182. d["Function"] = document.Reference(self.Function)
  2183. return self.ShadingDict(**d)
  2184. if __name__=="__main__":
  2185. print("There is no script interpretation for pdfdoc.")