PKDFdocumint/__init__.pyPKYF"›documint/mediumbox.py# From lp:~glyph/+junk/amphacks """ An argument type for sending medium-sized strings (more than 64k, but small enough that they still fit into memory and don't require streaming). """ from cStringIO import StringIO from itertools import count from twisted.protocols.amp import AMP, Argument, Command CHUNK_MAX = 0xffff class BigString(Argument): def fromBox(self, name, strings, objects, proto): value = StringIO() value.write(strings.get(name)) for counter in count(2): chunk = strings.get("%s.%d" % (name, counter)) if chunk is None: break value.write(chunk) objects[name] = value.getvalue() def toBox(self, name, strings, objects, proto): value = StringIO(objects[name]) firstChunk = value.read(CHUNK_MAX) strings[name] = firstChunk counter = 2 while True: nextChunk = value.read(CHUNK_MAX) if not nextChunk: break strings["%s.%d" % (name, counter)] = nextChunk counter += 1 class Send(Command): arguments = [('big', BigString())] class Example(AMP): @Send.responder def gotBig(self, big): print 'Got a big input', len(big) f = file("OUTPUT", "wb") f.write(big) f.close() return {} def main(argv): from twisted.internet import reactor from twisted.internet.protocol import Factory, ClientCreator if argv[1] == 'client': filename = argv[2] def connected(result): result.callRemote(Send, big=file(filename).read()) ClientCreator(reactor, AMP).connectTCP("localhost", 4321).addCallback( connected) reactor.run() elif argv[1] == 'server': f = Factory() f.protocol = Example reactor.listenTCP(4321, f) reactor.run() else: print "Specify 'client' or 'server'." if __name__ == '__main__': from sys import argv as arguments main(arguments) PKYF documint/util.py""" I{Documint} utility functions. """ from StringIO import StringIO from lxml import etree from twisted.internet.defer import maybeDeferred def embedStylesheets(markup, stylesheets, removeStylesheets=False): """ Embed external stylesheets in I{XHTML} markup. @type markup: L{str} @param markup: I{XHTML} markup UTF-8 byte data. @type stylesheets: I{iterable} of L{str} @param stylesheets: Iterable of stylesheet UTF-8 byte data to embed. @rtype: L{str} @return: I{XHTML} UTF-8 byte data with embedded stylesheets. """ tree = etree.parse(StringIO(markup)) namespaces = {'xhtml': 'http://www.w3.org/1999/xhtml'} head = tree.xpath('//xhtml:head', namespaces=namespaces)[0] for stylesheet in stylesheets: e = etree.SubElement( head, '{http://www.w3.org/1999/xhtml}style', attrib=dict(type='text/css')) e.text = stylesheet.decode('utf-8') if removeStylesheets: stylesheetLinks = head.xpath( 'xhtml:link[@rel="stylesheet"]', namespaces=namespaces) for e in stylesheetLinks: head.remove(e) return etree.tostring(tree, encoding='utf-8') def defertee(result, func, *a, **kw): """ Call C{func}, with positional and keyword arguments, as a side effect, and return C{result}. Useful in combination with C{Deferred.addCallback} when you wish to perform an operation in the callback chain but wish to retain the result for subsequent callbacks. """ d = maybeDeferred(func, result, *a, **kw) d.addCallback(lambda ignored: result) return d __all__ = ['embedStylesheets'] PKXF(.~??documint/client.py""" I{Documint} I{AMP} client. """ from twisted.internet import task from twisted.internet.endpoints import clientFromString, connectProtocol from twisted.protocols.amp import AMP from documint.commands import Certify, Render def render(protocol, markup, stylesheets): """ Execute the L{Render} AMP command. """ return protocol.callRemote( Render, markup=markup, stylesheets=stylesheets) def certify(protocol, data, contentType, reason, location): """ Execute the L{Certify} AMP command. """ return protocol.callRemote( Certify, data=data, contentType=contentType, reason=reason, location=location) __all__ = ['render', 'certify'] if __name__ == '__main__': import sys def main(reactor, markup, styles): def _readFile(name): with file(name, 'rb') as fd: return fd.read() def _writeResponse(response): sys.stdout.write(response['data']) sys.stdout.flush() endpoint = clientFromString(reactor, 'tcp:host=127.0.0.1:port=8750') d = connectProtocol(endpoint, AMP()) d.addCallback(render, _readFile(markup), map(_readFile, styles)) d.addCallback(_writeResponse) return d task.react(main, (sys.argv[1], sys.argv[2:])) PKXFV6documint/commands.py""" Documint I{AMP} command and protocol definitions. """ from lxml import etree from twisted.internet.defer import succeed from twisted.protocols import amp from twisted.python.failure import Failure from documint.errors import ( ExternalProcessError, RemoteExternalProcessError, UnsupportedContentType, XMLSyntaxError) from documint.extproc.css2xslfo import renderXHTML from documint.extproc.neon import failingPDFSign from documint.mediumbox import BigString from documint.util import embedStylesheets class Render(amp.Command): """ Render I{XHTML} markup and I{CSS} documents to a I{PDF} document. Accepts the following arguments: * C{markup}, a L{str}, that is the I{XHTML} markup byte data; * C{stylesheets}, a L{list} of L{str}, that is a list of stylesheet byte data. Returns a L{dict} mapping C{'data'} to a C{str} of rendered byte data, and C{'contentType'} to the content type of the byte data. """ arguments = [ ('markup', BigString()), ('stylesheets', amp.ListOf(amp.String()))] response = [ ('data', BigString()), ('contentType', amp.String())] errors = { XMLSyntaxError: 'XML_SYNTAX_ERROR', RemoteExternalProcessError: 'EXTERNAL_PROCESS_ERROR'} class Certify(amp.Command): """ Sign a I{PDF} document with the configured keystore. """ arguments = [ ('data', BigString()), ('contentType', amp.String()), ('reason', amp.String()), ('location', amp.String())] response = [ ('data', BigString()), ('contentType', amp.String())] errors = { UnsupportedContentType: 'UNSUPPORTED_CONTENT_TYPE', RemoteExternalProcessError: 'EXTERNAL_PROCESS_ERROR'} class Minter(amp.CommandLocator): """ Documint command locator. This implementation is for performing the full range of tasks that produce PDF documents. """ def __init__(self, signPDF=failingPDFSign): """ @param signPDF: Something like L{documint.extproc.neon.signPDF} with the keystore, keystore password and private key parameters already partially applied. """ self._signPDF = signPDF def embedStylesheets(self, markup, stylesheets): """ Embed external stylesheets in I{XHTML} markup. @type markup: L{str} @param markup: I{XHTML} markup byte data. @type stylesheets: I{iterable} of L{str} @param stylesheets: Iterable of stylesheet byte data to embed. @rtype: L{str} @return: I{XHTML} markup with embedded stylesheets. """ try: return embedStylesheets(markup, stylesheets) except etree.XMLSyntaxError, e: raise XMLSyntaxError(e) def _handleExternalProcessError(self, f): """ Convert L{ExternalProcessError} into L{RemoteExternalProcessError}. """ f.trap(ExternalProcessError) return Failure(RemoteExternalProcessError(f.getErrorMessage())) def _dataResult(self, (data, contentType)): """ Convert a 2-tuple of data and content type into a dict. """ return dict(data=data, contentType=contentType) def _renderXHTML(self, markup, stylesheets): """ Render I{XHMTL} markup and I{CSS} to a I{PDF}. @type markup: L{str} @param markup: I{XHTML} UTF-8 byte data. @type stylesheets: I{iterable} of L{str} @param stylesheets: Iterable of stylesheet UTF-8 byte data to embed. @rtype: L{Deferred} firing with C{(str, str)} @return: Deferred that fires with the generated I{PDF} byte data and content type. """ d = renderXHTML(self.embedStylesheets(markup, stylesheets)) d.addErrback(self._handleExternalProcessError) d.addCallback(lambda data: (data, 'application/pdf')) return d @Render.responder def render(self, markup, stylesheets): d = self._renderXHTML(markup, stylesheets) d.addCallback(self._dataResult) return d def _certifyDocument(self, data, contentType, reason, location): """ Sign a I{PDF} document. """ if contentType != 'application/pdf': raise UnsupportedContentType( 'Only PDF content can be certified, got: {!r}'.format(contentType)) d = self._signPDF(data=data, reason=reason, location=location) d.addErrback(self._handleExternalProcessError) d.addCallback(lambda data: (data, 'application/pdf')) return d @Certify.responder def certify(self, data, contentType, reason, location): d = self._certifyDocument(data, contentType, reason, location) d.addCallback(self._dataResult) return d class SimpleMinter(Minter): """ Documint command locator. The main difference between L{SimpleMinter} and L{Minter} is that L{SimpleMinter} will output I{XHTML}. """ def _renderXHTML(self, markup, stylesheets): """ Embed I{CSS} in I{XHMTL} markup @type markup: L{str} @param markup: I{XHTML} UTF-8 byte data. @type stylesheets: I{iterable} of L{str} @param stylesheets: Iterable of stylesheet UTF-8 byte data to embed. @rtype: L{Deferred} firing with C{(str, str)} @return: Deferred that fires with the generated I{XHTML}, including embedded I{CSS} byte data, and the content type. """ return succeed( (self.embedStylesheets(markup, stylesheets), 'text/html')) __all__ = ['Render', 'Minter', 'SimpleMinter', 'Certify'] PKXF documint/errors.py""" I{Documint} error types. """ class XMLSyntaxError(Exception): """ Wrapper around L{lxml.etree.XMLSyntaxError} that requires no additional arguments. """ class ExternalProcessError(RuntimeError): """ An external process returned an exit status indicating failure. @type binary: C{str} @ivar binary: Path to the binary to spawn. @type arguments: C{sequence} of C{str} @ivar arguments: Arguments to pass when spawning L{binary}. @type code: C{int} @ivar code: Exit status. @type stdout: C{str} @ivar stdout: Standard output data. @type stderr: C{str} @ivar stderr: Standard error data. """ def __init__(self, binary, arguments, code, (stdout, stderr)): RuntimeError.__init__(self, (binary, arguments, code, (stdout, stderr))) self.binary = binary self.arguments = arguments self.code = code self.stdout = stdout self.stderr = stderr class RemoteExternalProcessError(Exception): """ AMP-friendly description of an external process error. """ class NoSuchFile(IOError): """ The specified file could not be found. """ class UnsupportedContentType(ValueError): """ The specified content type is not supported. """ PKDFdocumint/test/__init__.pyPKYF2documint/test/test_util.py""" Tests for L{documint.util}. """ from StringIO import StringIO from lxml import etree from twisted.trial.unittest import TestCase from documint.util import embedStylesheets class EmbedStylesheetsTests(TestCase): """ Tests for L{documint.util.embedStylesheets}. """ def test_removeStylesheetLinks(self): """ L{documint.util.embedStylesheets} removes links with the C{'stylesheet'} relationship. """ markup = embedStylesheets(''' ''', [], removeStylesheets=True) tree = etree.parse(StringIO(markup)) namespaces = {'xhtml': 'http://www.w3.org/1999/xhtml'} elems = tree.findall('//xhtml:head/xhtml:link', namespaces=namespaces) self.assertEquals(1, len(elems)) self.assertEquals('something', elems[0].get('rel')) def test_embedStylesheets(self): """ L{documint.util.embedStylesheets} embeds stylesheets in I{style} elements. """ stylesheets = [ 'div {color:red;}', 'span {color:blue;}'] markup = embedStylesheets(''' ''', stylesheets) tree = etree.parse(StringIO(markup)) namespaces = {'xhtml': 'http://www.w3.org/1999/xhtml'} elems = tree.findall('//xhtml:head/xhtml:style', namespaces=namespaces) self.assertEquals(2, len(elems)) for elem, stylesheet in zip(elems, stylesheets): self.assertEquals(stylesheet, elem.text) PKYFyvDDdocumint/test/test_css2xslfo.pyfrom twisted.internet.defer import fail, succeed from twisted.python.filepath import FilePath from twisted.trial.unittest import TestCase from documint.errors import ExternalProcessError from documint.extproc.css2xslfo import css2xslfo, findCSS2XSLFO, renderXHTML class CSS2XSLFOTests(TestCase): """ Tests for L{documint.util.css2xslfo}. """ try: findCSS2XSLFO() except RuntimeError: skip = 'css2xslfo unavailable' def setUp(self): self.dataPath = FilePath(__file__).sibling('data') def test_missing(self): """ Invoking I{css2xslfo} with a missing input raises an L{ExternalProcessError}. """ def checkException(e): self.assertIn('(No such file or directory)', e.stderr) outputPath = FilePath(self.mktemp()) outputPath.touch() d = css2xslfo(self.dataPath.child('missing.html'), outputPath) d = self.assertFailure(d, ExternalProcessError) d.addCallback(checkException) return d def test_broken(self): """ Invoking I{css2xslfo} with a broken input raises an L{ExternalProcessError}. """ def checkException(e): self.assertIn( 'element type "div" must be terminated by the matching end-tag', e.stderr) outputPath = FilePath(self.mktemp()) outputPath.touch() d = css2xslfo(self.dataPath.child('broken.html'), outputPath) d = self.assertFailure(d, ExternalProcessError) d.addCallback(checkException) return d class RenderXHMTLTests(TestCase): """ Tests for L{documint.util.renderXHTML}. """ def mkdtemp(self): """ Create a temporary directory. @rtype: L{FilePath} """ tempDir = FilePath(self.mktemp()) if not tempDir.exists(): tempDir.makedirs() return tempDir def test_renderXHTML(self): """ L{renderXHTML} invokes L{css2xslfo} then L{fop}, removes the temporary directory, and returns I{fop}'s result. """ def mockCSS2XSLFO(xhtmlPath, xslfoPath): return succeed(None) def mockFop(xslfoPath, pdfPath, configFile=None): return succeed('pdf') def cb(pdfData): self.assertIdentical(str, type(pdfData)) self.assertEquals('pdf', pdfData) self.assertFalse(tempDir.exists()) tempDir = self.mkdtemp() d = renderXHTML( 'markup', tempDir=tempDir, css2xslfo=mockCSS2XSLFO, fop=mockFop) d.addCallback(cb) return d def test_renderXHTMLCSS2XSLFOFails(self): """ If L{renderXHTML} fails invoking L{css2xslfo}, L{fop} is not invoked and the temporary directory is not removed. """ def mockCSS2XSLFO(xslfoPath, xhtmlPath): return fail(RuntimeError(1)) def mockFop(xslfoPath, pdfPath, configFile=None): self.fail('Never get here') def cb(e): self.assertEquals('1', str(e)) self.assertTrue(tempDir.exists()) tempDir = self.mkdtemp() d = renderXHTML( 'markup', tempDir=tempDir, css2xslfo=mockCSS2XSLFO, fop=mockFop) d = self.assertFailure(d, RuntimeError) d.addCallback(cb) return d def test_renderXHTMLFopFails(self): """ If L{renderXHTML} fails invoking L{fop}, the temporary directory is not removed. """ def mockCSS2XSLFO(xhtmlPath, xslfoPath): return succeed(None) def mockFop(xslfoPath, pdfPath, configFile=None): return fail(RuntimeError(2)) def cb(e): self.assertEquals('2', str(e)) self.assertTrue(tempDir.exists()) tempDir = self.mkdtemp() d = renderXHTML( 'markup', tempDir=tempDir, css2xslfo=mockCSS2XSLFO, fop=mockFop) self.assertFailure(d, RuntimeError) d.addCallback(cb) return d PKXF;idocumint/test/test_commands.py""" Tests for L{documint.commands}. """ from twisted.internet.defer import succeed from twisted.trial.unittest import TestCase from documint.commands import Minter, SimpleMinter class MinterTests(TestCase): """ Tests for L{documint.commands.Minter}. """ def test_render(self): """ L{Minter.render}, a responder for L{documint.commands.Render}, invokes L{Minter.renderXHTML}. """ minter = Minter() minter._renderXHTML = lambda *a: succeed((a, 'application/pdf')) d = minter.render('markup', ['css1', 'css2']) d.addCallback( self.assertEquals, {'data': ('markup', ['css1', 'css2']), 'contentType': 'application/pdf'}) return d class SimpleMinterTests(TestCase): """ Tests for L{documint.commands.SimpleMinter}. """ def test_renderXHTML(self): """ L{SimpleMinter.renderXHTML} invokes L{Minter.embedStylesheets}. """ minter = SimpleMinter() minter.embedStylesheets = lambda *a: a d = minter._renderXHTML('markup', ['css1', 'css2']) d.addCallback( self.assertEquals, (('markup', ['css1', 'css2']), 'text/html')) return d PKaF6ׁdocumint/test/test_neon.pyfrom twisted.python.filepath import FilePath from twisted.trial.unittest import TestCase from documint.errors import ExternalProcessError from documint.extproc.neon import _neonBinary, signPDF class NeonTests(TestCase): """ Tests for L{documint.extproc.neon}. """ try: _neonBinary() except RuntimeError: skip = 'clj-neon unavailable.' def setUp(self): self.keystore = FilePath( __file__).sibling('data').child('keystore.jks') self.keystorePassword = u'tQ4i4RJKyX6J4Lq1' self.privateKeyPassword = u'tQ4i4RJKyX6J4Lq1' def signPDF(self, unsignedPDF): return signPDF( data=unsignedPDF, keystorePath=self.keystore, keystorePassword=self.keystorePassword, reason='Test reason', location='Test location', privateKeyPassword=self.privateKeyPassword) def assertValidPDF(self, data): """ Assert that C{data} is valid PDF data. """ self.assertNotEqual(len(data), 0) self.assertTrue(data.startswith('%PDF-')) def test_success(self): """ Neon generates a valid document when invoked with valid data. """ unsignedPDF = FilePath(__file__).sibling('data').child('test.pdf') d = self.signPDF(unsignedPDF.getContent()) d.addCallback(self.assertValidPDF) return d def test_failure(self): """ Invoking Neon with invalid data raises L{documint.error.ExternalProcessError}. """ d = self.signPDF('garbage') return self.assertFailure(d, ExternalProcessError) PKFFdocumint/test/data/broken.html Broken test documint
PKaFYxdocumint/test/data/keystore.jksdarren's globalsign nv-sa idFBywa00 +*̈́ (1eҠR%?xNRnN# %Z?vIᅫ1}Clx1Jz4+lT޳0F2<]ſ_*q C>]>L7 `jb$txK943k5l} wb(B49Cz3l*qc\\dFr=ZW`zG *L`ZoMRܙd"=3 u0!W|N2V.<ʼ-Fh-b pFazj݁i-ƠU6<cIcU&)bv?w3q.򈕄w˂`SƗߔsGL2',-Hd|(oT7%#9quGsCVřRMDYk:!7BYX"m3# u 5Zlzj,r95^:[DpH6KiMjVԹ?ù(qєW&nS>Fp[ws 4xGK@xK'BU3(nΈu}_J[hIB`j iF,BYB?[͑S2H0=xW~ybY"es2J [,21wmYXoEV23m,\]bBұ5.*O ? Z';N{ 7os.hg/UFSYhJ/ZecvLZY5SsyqKѢI>l;La#ǙWƧONk8(;@eĖpđ*i؆3 B{JRuF5}qHK!/ -Y z]S>?t)~]="˥zEvY1v ?_tfT {B;sZ,%#_.ZLg`4? oHyk1tO0kA {cdAJ%b u b=.yF>2`^=X.50900!qO^[Z}@[jdT7P /NW =C}3t]N׼)NXdbBFDF7@l|dFs(`/=ob0^0U0U 00 *H/00+0This certificate has been issued for the sole purpose of conducting quality assurance testing and should not be trusted or relied upon02+&https://www.globalsign.com/repository/0 U00U% 0  *H/0=U604020.,http://crl.globalsign.com/gs/gssha2adobe.crl0O+C0A0?+03http://secure.globalsign.com/cacert/gssha2adobe.der0Z *H/ L0JEhttp://adobe-timestamp.globalsign.com/tsa/aohfewat2389535fnasgnlg5m230 *H/ 00UGB-8l0U#0C#?Iso*+"1?\0  *H  GC]\c|I!k3êaLOlN/U~ز<05dQ3K V9OC¿eN&(9h*1}:rVsrk\ #eL`؎E]CS6(MAM.A/ASXda|L}OA@5 bбV僪x󡎅jOp3EE[IWR7\oQpl|6uXj$%!X.509b0^0F!Ljx>0  *H  0r1 0 UBE10U GlobalSign nv-sa10U GlobalSign CDS1/0-U&GlobalSign Primary SHA256 CA for Adobe0 110623000000Z 190623000000Z0j1 0 UBE10U GlobalSign nv-sa10U GlobalSign CDS1'0%UGlobalSign SHA256 CA for Adobe0"0  *H 0 .%=wMjvߠREZfZ,oP{N/5P oʩ]ni0ʌC"{6/\&L-k=C zCċ0DQzVͺ̣/cO3Nf2tۀ'$oۺeLdᛈκUD_mY fy2W,@; );s`ΰA%Zo"tUde'00U0U 00 *H/002+&https://www.globalsign.com/repository/0+0~|This certificate has been issued in accordance with the GlobalSign CDS CPS located at https://www.globalsign.com/repository/0U00U% 0  *H/0@U9070531/http://crl.globalsign.com/gs/gsprmsha2adobe.crl0R+F0D0B+06http://secure.globalsign.com/cacert/gsprmsha2adobe.der0UC#?Iso*+"1?\0U#0Y$kBԕv)\A+$_0  *H  Lش^ߘIw[e$a0dLJF V5a) :gȑڣJaԚ!0Y01ԙSfXJb-c3b`7ڤyjW~FwET@tG0G[LJϧlzF~AI?aWSzYəo'OmYfr=XDqSsqc0#21F0cɞI-*Q.ʵa8poCu-+QtX.509005䰒'l1γ0  *H  0i1 0 UUS1#0!U Adobe Systems Incorporated10U Adobe Trust Services10U Adobe Root CA0 110525000000Z 220524235959Z0r1 0 UBE10U GlobalSign nv-sa10U GlobalSign CDS1/0-U&GlobalSign Primary SHA256 CA for Adobe0"0  *H 0  UZ6-#*7 5i6SVZF8rv]ŃRZ7/mE50;\柩RV9f/=W.k-=gQlџl56wgt76@IN3r>omBdiοԒ-n n򿇹hUڒ$aƞrr 2:c=\pWMc}(k@*.tn8Uxdm00U00U 00 *H/006+*https://www.adobe.com/misc/pki/cds_cp.html0+0~The certificate has been issued in conformance with the Certificate Policy found at https://www.adobe.com/misc/pki/cds_cp.html0-U&0$0" http://crl.adobe.com/cds.crl0U% 0  *H/0 U0UY$kBԕv)\A+$_0U#08JT0  *H   ~>GD8~6^`eb#Gu[?&uY X?Z(Vd n}޽W׀){WEhO L-s*`U! ,?qḭ7qNfM IJ<}\:Gɚ[3n,5qRٚp~-}f,E79gC:,=2\=1Jߒ[}_  ȱ a+Lp^Ho: 'ZDa$ܸz^}8ڀLPKaF [L;L;documint/test/data/test.pdf%PDF-1.4 % 25 0 obj <> endobj xref 25 20 0000000016 00000 n 0000001051 00000 n 0000001334 00000 n 0000001492 00000 n 0000001814 00000 n 0000002227 00000 n 0000002585 00000 n 0000002628 00000 n 0000003779 00000 n 0000004128 00000 n 0000006797 00000 n 0000006831 00000 n 0000007014 00000 n 0000008350 00000 n 0000008592 00000 n 0000008819 00000 n 0000009040 00000 n 0000009082 00000 n 0000000885 00000 n 0000000696 00000 n trailer <]>> startxref 0 %%EOF 44 0 obj<>stream xb`````d Y8AF)»&\Рgb`P} ,h2'f7<" \l endstream endobj 43 0 obj<>/Size 25/Type/XRef>>stream xbbbd`b``9 endstream endobj 26 0 obj<>/Metadata 5 0 R/PieceInfo<>>>/Pages 4 0 R/PageLayout/OneColumn/OCProperties<>/StructTreeRoot 7 0 R/Type/Catalog/LastModified(D:20061122124941)/PageLabels 2 0 R>> endobj 27 0 obj<>/PageElement<>>>/Name(HeaderFooter)/Type/OCG>> endobj 28 0 obj<>/ColorSpace<>/Font<>/ProcSet[/PDF/Text/ImageC/ImageI]/Properties<>/ExtGState<>>>/Type/Page>> endobj 29 0 obj<> endobj 30 0 obj<> endobj 31 0 obj[/Indexed 35 0 R 32 36 0 R] endobj 32 0 obj<>stream HVMo8WQ:) ( qvEf7^YږW_3CIS z8wD"S=ntj•Vqaz27EQA>uZɷ?P4*)~ +:/2Lz\d(rlyRl^q@l$HxxY| 2x`VG %sYuT3@<^A%髎,dׯxlPa=6 iK&\/dŹ iF.C^VQnUwe O-XOmAɱFF(bI^2-6Ǵ,H|ƺ2vsO7GtuVƤ`DZQ6M4 UUcV{K9oX>n,a=`}ˆ&8,CU.&Q%/;yM8w%5_u)iu wfU ~*)ܦq DiA౗T'?zDD|`K=T endstream endobj 33 0 obj<> endobj 34 0 obj<>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 35 0 obj[/ICCBased 34 0 R] endobj 36 0 obj<>stream Hc߻p0`@'B BZPPfk}Į5N]qx ,Ev endstream endobj 37 0 obj<>stream Hb0钶VŅ^+{&$Bί,g !Ik xO}'T x ;N4m1qWMLpؘ8@!/[~fJ}wO5׎m(5($oɍ6;,$BQ2gXH$uw3Q ad|QR8Fve L֭٭ʩ3eceBI}PuuJ1[y">;Wn4_$u&MaD!a* œ!HbR1w5NYEpgG$T{.Owb qWC4F.66ʀlNȵ e"rV a3Eɞ2gzJG.|bG3uCHlFlAK^4͵,\GJ,HĜ=̹eGrl\>?HnSB#ާI6cm$`F> *n#nٛH"'s%P$>ʞp$;]$HpKwtABFH ۟D34x$Q$aٴ$.îJÒϕ"q1l\DHT@ٴ2z܁P ’KAVHv!;"W6]\8G|p 8! fHM8lC𑬡֑%1)oI6Τa\TM.NwqFR:j'@#$VC3/I#-+צHlS0'קE0A sGzcbpS$W@3d*$6C y:C9#908p%z$Uba'2lcff.a >3b:J4ƞp7qs3cHJ=w2 g &wNtǺʵM - endstream endobj 38 0 obj<> endobj 39 0 obj<> endobj 40 0 obj<> endobj 41 0 obj<> endobj 42 0 obj<> endobj 1 0 obj<>stream xڴTao0+'&ucJ!Z}@K8r=;ФU٤n_޻;=GN(r x (xueLAHPpvM'NdD8" $AC6M5FvnB)Sl'jKֹLKW̷b5㮵@"kZ`nٌ[D--Hכ䋏56O :5.PCl]ņFgi^UAXR{P*+֟zYmp,l{Z5:Jeڎϊfؓ`RS_?mMxO.5٣.g6~64yI}^ mXF@)V4}> endobj 3 0 obj<> endobj 4 0 obj<> endobj 5 0 obj<>stream Acrobat Distiller 7.0.5 (Windows) D:20061115213210 Acrobat PDFMaker 7.0.7 for Word 2006-11-22T12:49:41+02:00 2006-11-22T12:49:38+02:00 2006-11-22T12:49:41+02:00 uuid:8ec5f720-faa6-4055-a6aa-65ec3fa4c3bb uuid:f9fb4744-333b-4b5a-8a2b-c659684023b2 1 application/pdf Microsoft Word - Matrix.doc endstream endobj 6 0 obj<> endobj xref 0 25 0000000000 65535 f 0000009158 00000 n 0000009890 00000 n 0000009923 00000 n 0000009946 00000 n 0000009997 00000 n 0000014368 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF PKŮFdocumint/extproc/__init__.pyPKYFZ documint/extproc/common.pyfrom os.path import exists from warnings import warn from twisted.internet.utils import getProcessOutputAndValue from twisted.python import log from twisted.python.filepath import FilePath from twisted.python.procutils import which as twisted_which from documint.errors import ExternalProcessError, NoSuchFile from documint.util import defertee def _checkStatus((out, err, code)): """ Check that the exit status is C{0}. """ return code == 0 def getProcessOutput(binary, args, getProcessOutputAndValue=getProcessOutputAndValue, checkStatus=_checkStatus): """ Spawn a new process and return the standard output. @type binary: C{str} @param binary: Path to the binary to spawn. @type args: C{sequence} of C{str} @param args: Arguments to pass when spawning L{binary}. @param getProcessOutputAndValue: Callable to spawn a process and return C{(standard output, standard error, exit status)}. Defaults to L{twisted.internet.utils.getProcessOutputAndValue} @raise L{ExternalProcessError}: If the exit status is non-zero. @rtype: C{Deferred} @return: Deferred firing with the standard output of L{binary}. """ def _log((out, err, code)): name = FilePath(binary).basename() log.msg('%s command: %r' % (name, (binary, args))) def _check((out, err, code)): if not checkStatus((out, err, code)): raise ExternalProcessError(binary, args, code, (out, err)) return out d = getProcessOutputAndValue(binary, args) d.addCallback(defertee, _log) d.addCallback(_check) return d def sanitizePaths(paths): """ Convert an iterable of C{FilePath}s to an iterable of C{str}. """ for path in paths: if isinstance(path, (str, unicode)): warn( 'paths should be twisted.python.filepath.FilePath not %r' % ( type(path),), DeprecationWarning, 2) path = FilePath(path) fn = path.path if isinstance(fn, unicode): fn = fn.encode('ascii') if not exists(fn): raise NoSuchFile(fn) yield fn def which(binary, _which=twisted_which): """ Locate the first binary in the path. @type binary: C{str} @param binary: Name of the binary to find. @param _which: Callable taking one parameter, the name of the binary to find, returning a list of possibilities. @raise RuntimeError: If the binary could not be located. @return: Path of the binary. """ paths = _which(binary) if paths: return paths[0] raise RuntimeError('binary %r not found' % (binary,)) PKYFjdocumint/extproc/fop.pyfrom functools import partial from documint.extproc.common import getProcessOutput, which findFop = partial(which, 'fop') def fop(inputFile, outputFile, configFile=None): """ Run Apache FOP. @type inputFile: L{twisted.python.filepath.FilePath} @param inputFile: Input file path. @type outputFile: L{twisted.python.filepath.FilePath} @param outputFile: Output file path. @type configFile: L{twisted.python.filepath.FilePath} @param configFile: Optional config file path. @raise L{ExternalProcessError}: If FOP fails to complete successfully. @rtype: C{Deferred} @return: Deferred that fires with the byte data of the resulting PDF document. """ def _readData(ignored): return outputFile.getContent() args = [] if configFile is not None: args.extend(['-c', configFile.path]) args.extend([inputFile.path, outputFile.path]) d = getProcessOutput(findFop(), args) d.addCallback(_readData) return d PKYF'kdocumint/extproc/css2xslfo.pyimport tempfile from functools import partial from os.path import expanduser from twisted.python.filepath import FilePath from documint.extproc.common import getProcessOutput, which from documint.extproc.fop import fop from documint.util import defertee FOP_CONFIG = FilePath(expanduser('~/.config/documint/fop.xconf')) findCSS2XSLFO = partial(which, 'css2xslfo') def css2xslfo(xhtmlPath, xslfoPath): """ Invoke I{CSS2XSLFO}. @type xhtmlPath: L{FilePath} @param xhtmlPath: Path to read I{XHTML} input from. @type xslfoPath: L{FilePath} @param xslfoPath: Path to write I{XSL-FO} output to. @raises ExternalProcessError: If I{CSS2XSLFO} did not exit successfully. @rtype: C{Deferred} @return: Deferred that fires when the process is complete. """ def checkStatus((out, err, code)): return xslfoPath.getsize() > 0 return getProcessOutput( findCSS2XSLFO(), ['-fo', xslfoPath.path, xhtmlPath.path], checkStatus=checkStatus) def renderXHTML(markup, tempDir=None, css2xslfo=css2xslfo, fop=fop): """ Render an I{XHTML} document to a I{PDF} document. @type markup: L{str} @param markup: I{XHTML} document, encoded as UTF-8, that includes stylesheet information. @rtype: L{Deferred} firing with L{str} @return: Deferred that fires with the generated I{PDF} byte data. """ def _removeTemp(ignored): tempDir.remove() if tempDir is None: tempDir = FilePath(tempfile.mkdtemp()) xhtmlPath = tempDir.child('input.html') xhtmlPath.setContent(markup) xslfoPath = tempDir.child('output.fo') pdfPath = tempDir.child('output.pdf') configPath = FOP_CONFIG if not configPath.exists(): configPath = None d = css2xslfo(xhtmlPath, xslfoPath) d.addCallback(lambda ignored: fop(xslfoPath, pdfPath, configPath)) d.addCallback(defertee, _removeTemp) return d __all__ = ['css2xslfo', 'renderXHTML'] PKXFH#7 documint/extproc/neon.pyfrom functools import partial from tempfile import mkstemp from twisted.python.filepath import FilePath from documint.errors import RemoteExternalProcessError from documint.extproc.common import getProcessOutput, sanitizePaths, which _neonBinary = partial(which, 'clj-neon') def failingPDFSign(*a, **kw): """ Fail to sign anything. """ raise RemoteExternalProcessError('PDF signing is not correctly configured') def signPDF(data, keystorePath, keystorePassword, reason, location, signaturePage=None, fields=None, privateKeyPassword=None, imagePath=None, rectangle=None): """ Digitally sign a PDF. @param data: Unsigned PDF bytes. @type data: L{str} @param keystorePath: The path to the Java Keystore. @type keystorePath: L{twisted.python.filepath.FilePath} @param keystorePassword: The Java Keystore password. @type keystorePassword: L{str} @param reason: The reason for signing the PDF. @type reason: L{str} @param location: The location the PDF was signed. @type location: L{str} @param signaturePage: Path to signature page. @type signaturePage: L{FilePath} or L{None} @param fields: Mapping of signature page field names and values. @type fields: L{dict} @param privateKeyPassword: The password for the private key contained in the Java Keystore. @type privateKeyPassword: L{str} or L{None} @param imagePath: The path to an image to stamp on the PDF. @type imagePath: L{twisted.python.filepath.FilePath} @param rectangle: The size of the signature rectangle. eg: [LX1,LY1,UX2,UY2] @type rectangle: L{list} of L{str} @return: A deferred resulting in the signed PDF content as a byte string or a L{diamond.error.ExternalProcessError}. """ tempPath = FilePath(mkstemp()[1]) tempPath.setContent(data) def _cleanup(result): tempPath.remove() return result keystorePath, inputPath = sanitizePaths([keystorePath, tempPath]) args = [inputPath, '-', keystorePath, '--keystore-pass', keystorePassword, '--reason', reason, '--location', location] if privateKeyPassword: args.extend(['--password', privateKeyPassword]) if imagePath: args.extend(['--signature-image', imagePath]) if rectangle: args.extend(['--signature-rect', ','.join(rectangle)]) if signaturePage: args.extend(['--signature-page', signaturePage.path]) if fields: for k, v in fields.iteritems(): args.extend(['--field', '%s:%s' % (k, v)]) d = getProcessOutput(_neonBinary(), args) d.addBoth(_cleanup) return d PKXF'hh#twisted/plugins/documint_service.pyfrom functools import partial from twisted.application import strports from twisted.application.service import IServiceMaker from twisted.internet import reactor from twisted.internet.protocol import Factory from twisted.plugin import IPlugin from twisted.protocols.amp import AMP from twisted.python import usage from zope.interface import implementer from documint.commands import Minter from documint.extproc.neon import signPDF, failingPDFSign class Options(usage.Options): optParameters = [ ['port', 'p', 'tcp:8750', 'Documint service strport description'], ['keystore', None, None, 'Java keystore path'], ['password', None, None, 'Keystore password'], ['privateKeyPassword', None, None, 'Password for the private key in the keystore']] @implementer(IServiceMaker, IPlugin) class DocumintServiceMaker(object): tapname = 'documint' description = 'Document creation service' options = Options def _signPDF(self, options): """ L{documint.extproc.neon.signPDF} with keystore, keystore password and private key password partially applied. """ keystorePath = options['keystore'] if keystorePath is None: return failingPDFSign keystorePassword = options['password'] if keystorePassword is None: return failingPDFSign privateKeyPassword = options['privateKeyPassword'] return partial( signPDF, keystorePath=keystorePath, keystorePassword=keystorePassword, privateKeyPassword=privateKeyPassword) def makeService(self, options): factory = Factory() factory.protocol = lambda: AMP( locator=Minter(signPDF=self._signPDF(options))) return strports.service(options['port'], factory, reactor=reactor) serviceMaker = DocumintServiceMaker() PKcF^- )Documint-15.7.0.dist-info/DESCRIPTION.rstUNKNOWN PKcF#mKK'Documint-15.7.0.dist-info/metadata.json{"classifiers": ["Development Status :: 3 - Alpha", "Framework :: Twisted", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Topic :: Text Processing :: Markup :: HTML"], "extensions": {"python.details": {"document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/fusionapp/documint"}}}, "extras": [], "generator": "bdist_wheel (0.24.0)", "metadata_version": "2.0", "name": "Documint", "run_requires": [{"requires": ["Twisted", "lxml"]}], "summary": "UNKNOWN", "version": "15.7.0"}PKcFwu 'Documint-15.7.0.dist-info/top_level.txtdocumint twisted PKcF4\\Documint-15.7.0.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.24.0) Root-Is-Purelib: true Tag: py2-none-any PKcFʄIo"Documint-15.7.0.dist-info/METADATAMetadata-Version: 2.0 Name: Documint Version: 15.7.0 Summary: UNKNOWN Home-page: https://github.com/fusionapp/documint Author: UNKNOWN Author-email: UNKNOWN License: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Framework :: Twisted Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 Classifier: Topic :: Text Processing :: Markup :: HTML Requires-Dist: Twisted Requires-Dist: lxml UNKNOWN PKcF}mm Documint-15.7.0.dist-info/RECORDdocumint/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 documint/mediumbox.py,sha256=nLM3ROW3f9qgJuANPzobrNxbT4T62p90BTPf2a_6Ykg,2019 documint/util.py,sha256=hHG3Cf74hN59GCTmXknQ2yhVXjCvrVD5shXjMyeu5yE,1666 documint/client.py,sha256=EJ16VcD4x3Mpr1rb8bFPqPpZ2FvYA5LSe5E4vA_Tpis,1343 documint/commands.py,sha256=zS7HDdn5SB0khg3-8evLbUQkSk0txJGpXaZ4Ndy4_AU,5774 documint/errors.py,sha256=BW6Hx_hGBFe9UhKlLS8LOrnhpjX0g1FW1Yj9b9zfzKY,1286 documint/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 documint/test/test_util.py,sha256=F2oXKo5Vg5Fzi1pAkGCtC9EYZzFldMbyTUVv5-rZG2E,1960 documint/test/test_css2xslfo.py,sha256=hFMoNZdHypIbOM-FpLe4NnaOb7WjQ83daQ1MAo0jqD4,4164 documint/test/test_commands.py,sha256=lnMnXnnaST8tLjVL_XlB_jOg0gBh87TC96ffHb4gLU8,1240 documint/test/test_neon.py,sha256=ME1Ffs0Gi2FbDw2z7iEgTmQAh2MPBb2Xh6bZ7niSSNI,1665 documint/test/data/broken.html,sha256=aEu0zvRdTGEUHnK9T6tP79nJJ9HbuFA8go75ZwKnbo8,508 documint/test/data/keystore.jks,sha256=ncEJABz7DiMVjohh9GkkztHJV1ZZPoGWLiOvsfEvdeQ,5615 documint/test/data/test.pdf,sha256=mzGBj7-FQSS2IHNuqpeZxhtwkwppZFB1dnFB000-Wvg,15180 documint/extproc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 documint/extproc/common.py,sha256=HbBwILSsi7QMB9xWD4osL9ymA1uA7nKC0IuFW3-ddPE,2745 documint/extproc/fop.py,sha256=duSLMi0w8XfP0Qo873C15qk0keaHU1Gj7LyrnuVtX60,1013 documint/extproc/css2xslfo.py,sha256=s8jFCTAZO9AknOK2OBcbKAhbx5FeEa_9Yxw4eqKc4YA,1991 documint/extproc/neon.py,sha256=hffKOt8ZTG11YTL9c0V3wYlT4_ZlROTFFZpwaJJR0dM,2744 twisted/plugins/documint_service.py,sha256=VnhNRTn5GQ-rY8Ls8I9-9KKvKcS2nFybPfSGJB0N988,1896 Documint-15.7.0.dist-info/top_level.txt,sha256=SWUqdI6jkucqbJeM0DYWp9g3uOr6rC4i-yaIC-XuVss,17 Documint-15.7.0.dist-info/METADATA,sha256=oAUo8qcH2bbJghwPYkrtQqI1_ysFDiyvP3fN5wjaFWY,519 Documint-15.7.0.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 Documint-15.7.0.dist-info/metadata.json,sha256=1sBRqLh12zZQDgeRRK6eR_hpPm6JFeoUnJzhMBs_ErI,587 Documint-15.7.0.dist-info/WHEEL,sha256=54bVun1KfEBTJ68SHUmbxNPj80VxlQ0sHi4gZdGZXEY,92 Documint-15.7.0.dist-info/RECORD,, PKDFdocumint/__init__.pyPKYF"›2documint/mediumbox.pyPKYF Hdocumint/util.pyPKXF(.~??documint/client.pyPKXFV6gdocumint/commands.pyPKXF '+documint/errors.pyPKDF]0documint/test/__init__.pyPKYF20documint/test/test_util.pyPKYFyvDDt8documint/test/test_css2xslfo.pyPKXF;iHdocumint/test/test_commands.pyPKaF6ׁ Ndocumint/test/test_neon.pyPKFFTdocumint/test/data/broken.htmlPKaFYxVdocumint/test/data/keystore.jksPKaF [L;L;&mdocumint/test/data/test.pdfPKŮFdocumint/extproc/__init__.pyPKYFZ documint/extproc/common.pyPKYFjֳdocumint/extproc/fop.pyPKYF'kdocumint/extproc/css2xslfo.pyPKXFH#7 documint/extproc/neon.pyPKXF'hh#twisted/plugins/documint_service.pyPKcF^- )Documint-15.7.0.dist-info/DESCRIPTION.rstPKcF#mKK'Documint-15.7.0.dist-info/metadata.jsonPKcFwu 'zDocumint-15.7.0.dist-info/top_level.txtPKcF4\\Documint-15.7.0.dist-info/WHEELPKcFʄIo"iDocumint-15.7.0.dist-info/METADATAPKcF}mm Documint-15.7.0.dist-info/RECORDPK|[