diff --git a/bin/reportlab/demos/colors/colortest.py b/bin/reportlab/demos/colors/colortest.py new file mode 100644 index 00000000000..7845c2cd620 --- /dev/null +++ b/bin/reportlab/demos/colors/colortest.py @@ -0,0 +1,105 @@ +#Copyright ReportLab Europe Ltd. 2000-2004 +#see license.txt for license details +#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/colors/colortest.py +import reportlab.pdfgen.canvas +from reportlab.lib import colors +from reportlab.lib.units import inch + + +def run(): + c = reportlab.pdfgen.canvas.Canvas('colortest.pdf') + + #do a test of CMYK interspersed with RGB + + #first do RGB values + framePage(c, 'Color Demo - RGB Space and CMYK spaces interspersed' ) + + y = 700 + + c.setFillColorRGB(0,0,0) + c.drawString(100, y, 'cyan') + c.setFillColorCMYK(1,0,0,0) + c.rect(200, y, 300, 30, fill=1) + y = y - 40 + + c.setFillColorRGB(0,0,0) + c.drawString(100, y, 'red') + c.setFillColorRGB(1,0,0) + c.rect(200, y, 300, 30, fill=1) + y = y - 40 + + c.setFillColorRGB(0,0,0) + c.drawString(100, y, 'magenta') + c.setFillColorCMYK(0,1,0,0) + c.rect(200, y, 300, 30, fill=1) + y = y - 40 + + c.setFillColorRGB(0,0,0) + c.drawString(100, y, 'green') + c.setFillColorRGB(0,1,0) + c.rect(200, y, 300, 30, fill=1) + y = y - 40 + + c.setFillColorRGB(0,0,0) + c.drawString(100, y, 'yellow') + c.setFillColorCMYK(0,0,1,0) + c.rect(200, y, 300, 30, fill=1) + y = y - 40 + + c.setFillColorRGB(0,0,0) + c.drawString(100, y, 'blue') + c.setFillColorRGB(0,0,1) + c.rect(200, y, 300, 30, fill=1) + y = y - 40 + + c.setFillColorRGB(0,0,0) + c.drawString(100, y, 'black') + c.setFillColorCMYK(0,0,0,1) + c.rect(200, y, 300, 30, fill=1) + y = y - 40 + + + c.showPage() + + #do all named colors + framePage(c, 'Color Demo - RGB Space - page %d' % c.getPageNumber()) + + all_colors = reportlab.lib.colors.getAllNamedColors().items() + all_colors.sort() # alpha order by name + c.setFont('Times-Roman', 12) + c.drawString(72,730, 'This shows all the named colors in the HTML standard.') + y = 700 + for (name, color) in all_colors: + c.setFillColor(colors.black) + c.drawString(100, y, name) + c.setFillColor(color) + c.rect(200, y-10, 300, 30, fill=1) + y = y - 40 + if y < 100: + c.showPage() + framePage(c, 'Color Demo - RGB Space - page %d' % c.getPageNumber()) + y = 700 + + + + + c.save() + +def framePage(canvas, title): + canvas.setFont('Times-BoldItalic',20) + canvas.drawString(inch, 10.5 * inch, title) + + canvas.setFont('Times-Roman',10) + canvas.drawCentredString(4.135 * inch, 0.75 * inch, + 'Page %d' % canvas.getPageNumber()) + + #draw a border + canvas.setStrokeColorRGB(1,0,0) + canvas.setLineWidth(5) + canvas.line(0.8 * inch, inch, 0.8 * inch, 10.75 * inch) + #reset carefully afterwards + canvas.setLineWidth(1) + canvas.setStrokeColorRGB(0,0,0) + +if __name__ == '__main__': + run() diff --git a/bin/reportlab/demos/gadflypaper/00readme.txt b/bin/reportlab/demos/gadflypaper/00readme.txt new file mode 100644 index 00000000000..f50a9966846 --- /dev/null +++ b/bin/reportlab/demos/gadflypaper/00readme.txt @@ -0,0 +1,3 @@ +This is Aaron Watters' first script; +it renders his paper for IPC8 into +PDF. A fascinating read, as well. \ No newline at end of file diff --git a/bin/reportlab/demos/gadflypaper/gfe.py b/bin/reportlab/demos/gadflypaper/gfe.py new file mode 100644 index 00000000000..dfc1259d5a8 --- /dev/null +++ b/bin/reportlab/demos/gadflypaper/gfe.py @@ -0,0 +1,903 @@ +#Copyright ReportLab Europe Ltd. 2000-2004 +#see license.txt for license details +#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/gadflypaper/gfe.py +__version__=''' $Id: gfe.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' +__doc__='' + +#REPORTLAB_TEST_SCRIPT +import sys +from reportlab.platypus import * +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.rl_config import defaultPageSize +PAGE_HEIGHT=defaultPageSize[1] + +styles = getSampleStyleSheet() + +Title = "Integrating Diverse Data Sources with Gadfly 2" + +Author = "Aaron Watters" + +URL = "http://www.chordate.com/" + +email = "arw@ifu.net" + +Abstract = """This paper describes the primative methods underlying the implementation +of SQL query evaluation in Gadfly 2, a database management system implemented +in Python [Van Rossum]. The major design goals behind +the architecture described here are to simplify the implementation +and to permit flexible and efficient extensions to the gadfly +engine. Using this architecture and its interfaces programmers +can add functionality to the engine such as alternative disk based +indexed table implementations, dynamic interfaces to remote data +bases or or other data sources, and user defined computations.""" + +from reportlab.lib.units import inch + +pageinfo = "%s / %s / %s" % (Author, email, Title) + +def myFirstPage(canvas, doc): + canvas.saveState() + #canvas.setStrokeColorRGB(1,0,0) + #canvas.setLineWidth(5) + #canvas.line(66,72,66,PAGE_HEIGHT-72) + canvas.setFont('Times-Bold',16) + canvas.drawString(108, PAGE_HEIGHT-108, Title) + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo) + canvas.restoreState() + +def myLaterPages(canvas, doc): + #canvas.drawImage("snkanim.gif", 36, 36) + canvas.saveState() + #canvas.setStrokeColorRGB(1,0,0) + #canvas.setLineWidth(5) + #canvas.line(66,72,66,PAGE_HEIGHT-72) + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "Page %d %s" % (doc.page, pageinfo)) + canvas.restoreState() + +def go(): + Elements.insert(0,Spacer(0,inch)) + doc = SimpleDocTemplate('gfe.pdf') + doc.build(Elements,onFirstPage=myFirstPage, onLaterPages=myLaterPages) + +Elements = [] + +HeaderStyle = styles["Heading1"] # XXXX + +def header(txt, style=HeaderStyle, klass=Paragraph, sep=0.3): + s = Spacer(0.2*inch, sep*inch) + Elements.append(s) + para = klass(txt, style) + Elements.append(para) + +ParaStyle = styles["Normal"] + +def p(txt): + return header(txt, style=ParaStyle, sep=0.1) + +#pre = p # XXX + +PreStyle = styles["Code"] + +def pre(txt): + s = Spacer(0.1*inch, 0.1*inch) + Elements.append(s) + p = Preformatted(txt, PreStyle) + Elements.append(p) + +#header(Title, sep=0.1. style=ParaStyle) +header(Author, sep=0.1, style=ParaStyle) +header(URL, sep=0.1, style=ParaStyle) +header(email, sep=0.1, style=ParaStyle) +header("ABSTRACT") +p(Abstract) + +header("Backgrounder") + +p("""\ +The term "database" usually refers to a persistent +collection of data. Data is persistent if it continues +to exist whether or not it is associated with a running +process on the computer, or even if the computer is +shut down and restarted at some future time. Database +management systems provide support for constructing databases, +maintaining databases, and extracting information from databases.""") +p("""\ +Relational databases manipulate and store persistent +table structures called relations, such as the following +three tables""") + +pre("""\ + -- drinkers who frequent bars (this is a comment) + select * from frequents + + DRINKER | PERWEEK | BAR + ============================ + adam | 1 | lolas + woody | 5 | cheers + sam | 5 | cheers + norm | 3 | cheers + wilt | 2 | joes + norm | 1 | joes + lola | 6 | lolas + norm | 2 | lolas + woody | 1 | lolas + pierre | 0 | frankies +) +""") +pre("""\ + -- drinkers who like beers + select * from likes + + DRINKER | PERDAY | BEER + =============================== + adam | 2 | bud + wilt | 1 | rollingrock + sam | 2 | bud + norm | 3 | rollingrock + norm | 2 | bud + nan | 1 | sierranevada + woody | 2 | pabst + lola | 5 | mickies + +""") +pre("""\ + -- beers served from bars + select * from serves + + BAR | QUANTITY | BEER + ================================= + cheers | 500 | bud + cheers | 255 | samadams + joes | 217 | bud + joes | 13 | samadams + joes | 2222 | mickies + lolas | 1515 | mickies + lolas | 333 | pabst + winkos | 432 | rollingrock + frankies | 5 | snafu +""") +p(""" +The relational model for database structures makes +the simplifying assumption that all data in a database +can be represented in simple table structures +such as these. Although this assumption seems extreme +it provides a good foundation for defining solid and +well defined database management systems and some +of the most successful software companies in the +world, such as Oracle, Sybase, IBM, and Microsoft, +have marketed database management systems based on +the relational model quite successfully. +""") +p(""" +SQL stands for Structured Query Language. +The SQL language defines industry standard +mechanisms for creating, querying, and modified +relational tables. Several years ago SQL was one +of many Relational Database Management System +(RDBMS) query languages in use, and many would +argue not the best on. Now, largely due +to standardization efforts and the +backing of IBM, SQL is THE standard way to talk +to database systems. +""") +p(""" +There are many advantages SQL offers over other +database query languages and alternative paradigms +at this time (please see [O'Neill] or [Korth and Silberschatz] +for more extensive discussions and comparisons between the +SQL/relational approach and others.) +""") +p(""" +The chief advantage over all contenders at this time +is that SQL and the relational model are now widely +used as interfaces and back end data stores to many +different products with different performance characteristics, +user interfaces, and other qualities: Oracle, Sybase, +Ingres, SQL Server, Access, Outlook, +Excel, IBM DB2, Paradox, MySQL, MSQL, POSTgres, and many +others. For this reason a program designed to use +an SQL database as its data storage mechanism can +easily be ported from one SQL data manager to another, +possibly on different platforms. In fact the same +program can seamlessly use several backends and/or +import/export data between different data base platforms +with trivial ease. +No other paradigm offers such flexibility at the moment. +""") +p(""" +Another advantage which is not as immediately +obvious is that the relational model and the SQL +query language are easily understood by semi-technical +and non-technical professionals, such as business +people and accountants. Human resources managers +who would be terrified by an object model diagram +or a snippet of code that resembles a conventional +programming language will frequently feel quite at +ease with a relational model which resembles the +sort of tabular data they deal with on paper in +reports and forms on a daily basis. With a little training the +same HR managers may be able to translate the request +"Who are the drinkers who like bud and frequent cheers?" +into the SQL query +""") +pre(""" + select drinker + from frequents + where bar='cheers' + and drinker in ( + select drinker + from likes + where beer='bud') +""") +p(""" +(or at least they have some hope of understanding +the query once it is written by a technical person +or generated by a GUI interface tool). Thus the use +of SQL and the relational model enables communication +between different communities which must understand +and interact with stored information. In contrast +many other approaches cannot be understood easily +by people without extensive programming experience. +""") +p(""" +Furthermore the declarative nature of SQL +lends itself to automatic query optimization, +and engines such as Gadfly can automatically translate a user query +into an optimized query plan which takes +advantage of available indices and other data characteristics. +In contrast more navigational techniques require the application +program itself to optimize the accesses to the database and +explicitly make use of indices. +""") + +# HACK +Elements.append(PageBreak()) + +p(""" +While it must be admitted that there are application +domains such as computer aided engineering design where +the relational model is unnatural, it is also important +to recognize that for many application domains (such +as scheduling, accounting, inventory, finance, personal +information management, electronic mail) the relational +model is a very natural fit and the SQL query language +make most accesses to the underlying data (even sophisticated +ones) straightforward. """) + +p("""For an example of a moderately +sophisticated query using the tables given above, +the following query lists the drinkers who frequent lolas bar +and like at least two beers not served by lolas +""") + +if 0: + go() + sys.exit(1) + +pre(""" + select f.drinker + from frequents f, likes l + where f.drinker=l.drinker and f.bar='lolas' + and l.beer not in + (select beer from serves where bar='lolas') + group by f.drinker + having count(distinct beer)>=2 +""") +p(""" +yielding the result +""") +pre(""" + DRINKER + ======= + norm +""") +p(""" +Experience shows that queries of this sort are actually +quite common in many applications, and are often much more +difficult to formulate using some navigational database +organizations, such as some "object oriented" database +paradigms. +""") +p(""" +Certainly, +SQL does not provide all you need to interact with +databases -- in order to do "real work" with SQL you +need to use SQL and at least one other language +(such as C, Pascal, C++, Perl, Python, TCL, Visual Basic +or others) to do work (such as readable formatting a report +from raw data) that SQL was not designed to do. +""") + +header("Why Gadfly 1?") + +p("""Gadfly 1.0 is an SQL based relational database implementation +implemented entirely in the Python programming language, with +optional fast data structure accellerators implemented in the +C programming language. Gadfly is relatively small, highly portable, +very easy to use (especially for programmers with previous experience +with SQL databases such as MS Access or Oracle), and reasonably +fast (especially when the kjbuckets C accellerators are used). +For moderate sized problems Gadfly offers a fairly complete +set of features such as transaction semantics, failure recovery, +and a TCP/IP based client/server mode (Please see [Gadfly] for +detailed discussion).""") + + +header("Why Gadfly 2?") + +p("""Gadfly 1.0 also has significant limitations. An active Gadfly +1.0 database keeps all data in (virtual) memory, and hence a Gadfly +1.0 database is limited in size to available virtual memory. Important +features such as date/time/interval operations, regular expression +matching and other standard SQL features are not implemented in +Gadfly 1.0. The optimizer and the query evaluator perform optimizations +using properties of the equality predicate but do not optimize +using properties of inequalities such as BETWEEN or less-than. +It is possible to add "extension views" to a Gadfly +1.0 database, but the mechanism is somewhat clumsy and indices +over extension views are not well supported. The features of Gadfly +2.0 discussed here attempt to address these deficiencies by providing +a uniform extension model that permits addition of alternate table, +function, and predicate implementations.""") + +p("""Other deficiencies, such as missing constructs like "ALTER +TABLE" and the lack of outer joins and NULL values are not +addressed here, although they may be addressed in Gadfly 2.0 or +a later release. This paper also does not intend to explain +the complete operations of the internals; it is intended to provide +at least enough information to understand the basic mechanisms +for extending gadfly.""") + + + + +p("""Some concepts and definitions provided next help with the description +of the gadfly interfaces. [Note: due to the terseness of this +format the ensuing is not a highly formal presentation, but attempts +to approach precision where precision is important.]""") + +header("The semilattice of substitutions") + +p("""Underlying the gadfly implementation are the basic concepts +associated with substitutions. A substitution is a mapping +of attribute names to values (implemented in gadfly using kjbuckets.kjDict +objects). Here an attribute refers to some sort of "descriptive +variable", such as NAME and a value is an assignment for that variable, +like "Dave Ascher". In Gadfly a table is implemented as a sequence +of substitutions, and substitutions are used in many other ways as well. +""") +p(""" +For example consider the substitutions""") + +pre(""" + A = [DRINKER=>'sam'] + B = [DRINKER=>'sam', BAR=>'cheers'] + C = [DRINKER=>'woody', BEER=>'bud'] + D = [DRINKER=>'sam', BEER=>'mickies'] + E = [DRINKER=>'sam', BAR=>'cheers', BEER=>'mickies'] + F = [DRINKER=>'sam', BEER=>'mickies'] + G = [BEER=>'bud', BAR=>'lolas'] + H = [] # the empty substitution + I = [BAR=>'cheers', CAPACITY=>300]""") + +p("""A trivial but important observation is that since substitutions +are mappings, no attribute can assume more than one value in a +substitution. In the operations described below whenever an operator +"tries" to assign more than one value to an attribute +the operator yields an "overdefined" or "inconsistent" +result.""") + +header("Information Semi-order:") + +p("""Substitution B is said to be +more informative than A because B agrees with all assignments +in A (in addition to providing more information as well). Similarly +we say that E is more informative than A, B, D, F. and H but E +is not more informative than the others since, for example G disagrees +with E on the value assigned to the BEER attribute and I provides +additional CAPACITY information not provided in E.""") + +header("Joins and Inconsistency:") + +p("""A join of two substitutions +X and Y is the least informative substitution Z such that Z is +more informative (or equally informative) than both X and Y. For +example B is the join of B with A, E is the join of B with D and""") + +pre(""" + E join I = + [DRINKER=>'sam', BAR=>'cheers', BEER=>'mickies', CAPACITY=>300]""") + +p("""For any two substitutions either (1) they disagree on the value +assigned to some attribute and have no join or (2) they agree +on all common attributes (if there are any) and their join is +the union of all (name, value) assignments in both substitutions. +Written in terms of kjbucket.kjDict operations two kjDicts X and +Y have a join Z = (X+Y) if and only if Z.Clean() is not None. +Two substitutions that have no join are said to be inconsistent. +For example I and G are inconsistent since they disagree on +the value assigned to the BAR attribute and therefore have no +join. The algebra of substitutions with joins technically defines +an abstract algebraic structure called a semilattice.""") + +header("Name space remapping") + +p("""Another primitive operation over substitutions is the remap +operation S2 = S.remap(R) where S is a substitution and R is a +graph of attribute names and S2 is a substitution. This operation +is defined to produce the substitution S2 such that""") + +pre(""" + Name=>Value in S2 if and only if + Name1=>Value in S and Name<=Name1 in R +""") + +p("""or if there is no such substitution S2 the remap value is said +to be overdefined.""") + +p("""For example the remap operation may be used to eliminate attributes +from a substitution. For example""") + +pre(""" + E.remap([DRINKER<=DRINKER, BAR<=BAR]) + = [DRINKER=>'sam', BAR=>'cheers'] +""") + +p("""Illustrating that remapping using the [DRINKER<=DRINKER, +BAR<=BAR] graph eliminates all attributes except DRINKER and +BAR, such as BEER. More generally remap can be used in this way +to implement the classical relational projection operation. (See [Korth and Silberschatz] +for a detailed discussion of the projection operator and other relational +algebra operators such as selection, rename, difference and joins.)""") + +p("""The remap operation can also be used to implement "selection +on attribute equality". For example if we are interested +in the employee names of employees who are their own bosses we +can use the remapping graph""") + +pre(""" + R1 = [NAME<=NAME, NAME<=BOSS] +""") + +p("""and reject substitutions where remapping using R1 is overdefined. +For example""") + +pre(""" + S1 = [NAME=>'joe', BOSS=>'joe'] + S1.remap(R1) = [NAME=>'joe'] + S2 = [NAME=>'fred', BOSS=>'joe'] + S2.remap(R1) is overdefined. +""") + +p("""The last remap is overdefined because the NAME attribute cannot +assume both the values 'fred' and 'joe' in a substitution.""") + +p("""Furthermore, of course, the remap operation can be used to +"rename attributes" or "copy attribute values" +in substitutions. Note below that the missing attribute CAPACITY +in B is effectively ignored in the remapping operation.""") + +pre(""" + B.remap([D<=DRINKER, B<=BAR, B2<=BAR, C<=CAPACITY]) + = [D=>'sam', B=>'cheers', B2=>'cheers'] +""") + +p("""More interestingly, a single remap operation can be used to +perform a combination of renaming, projection, value copying, +and attribute equality selection as one operation. In kjbuckets the remapper +graph is implemented using a kjbuckets.kjGraph and the remap operation +is an intrinsic method of kjbuckets.kjDict objects.""") + +header("Generalized Table Joins and the Evaluator Mainloop""") + +p("""Strictly speaking the Gadfly 2.0 query evaluator only uses +the join and remap operations as its "basic assembly language" +-- all other computations, including inequality comparisons and +arithmetic, are implemented externally to the evaluator as "generalized +table joins." """) + +p("""A table is a sequence of substitutions (which in keeping with +SQL semantics may contain redundant entries). The join between +two tables T1 and T2 is the sequence of all possible defined joins +between pairs of elements from the two tables. Procedurally we +might compute the join as""") + +pre(""" + T1JoinT2 = empty + for t1 in T1: + for t2 in T2: + if t1 join t2 is defined: + add t1 join t2 to T1joinT2""") + +p("""In general circumstances this intuitive implementation is a +very inefficient way to compute the join, and Gadfly almost always +uses other methods, particularly since, as described below, a +"generalized table" can have an "infinite" +number of entries.""") + +p("""For an example of a table join consider the EMPLOYEES table +containing""") + +pre(""" + [NAME=>'john', JOB=>'executive'] + [NAME=>'sue', JOB=>'programmer'] + [NAME=>'eric', JOB=>'peon'] + [NAME=>'bill', JOB=>'peon'] +""") + +p("""and the ACTIVITIES table containing""") + +pre(""" + [JOB=>'peon', DOES=>'windows'] + [JOB=>'peon', DOES=>'floors'] + [JOB=>'programmer', DOES=>'coding'] + [JOB=>'secretary', DOES=>'phone']""") + +p("""then the join between EMPLOYEES and ACTIVITIES must containining""") + +pre(""" + [NAME=>'sue', JOB=>'programmer', DOES=>'coding'] + [NAME=>'eric', JOB=>'peon', DOES=>'windows'] + [NAME=>'bill', JOB=>'peon', DOES=>'windows'] + [NAME=>'eric', JOB=>'peon', DOES=>'floors'] + [NAME=>'bill', JOB=>'peon', DOES=>'floors']""") + +p("""A compiled gadfly subquery ultimately appears to the evaluator +as a sequence of generalized tables that must be joined (in combination +with certain remapping operations that are beyond the scope of +this discussion). The Gadfly mainloop proceeds following the very +loose pseudocode:""") + +pre(""" + Subs = [ [] ] # the unary sequence containing "true" + While some table hasn't been chosen yet: + Choose an unchosen table with the least cost join estimate. + Subs = Subs joined with the chosen table + return Subs""") + +p("""[Note that it is a property of the join operation that the +order in which the joins are carried out will not affect the result, +so the greedy strategy of evaluating the "cheapest join next" +will not effect the result. Also note that the treatment of logical +OR and NOT as well as EXIST, IN, UNION, and aggregation and so +forth are not discussed here, even though they do fit into this +approach.]""") + +p("""The actual implementation is a bit more complex than this, +but the above outline may provide some useful intuition. The "cost +estimation" step and the implementation of the join operation +itself are left up to the generalized table object implementation. +A table implementation has the ability to give an "infinite" +cost estimate, which essentially means "don't join me in +yet under any circumstances." """) + +header("Implementing Functions") + +p("""As mentioned above operations such as arithmetic are implemented +using generalized tables. For example the arithmetic Add operation +is implemented in Gadfly internally as an "infinite generalized +table" containing all possible substitutions""") + +pre(""" + ARG0=>a, ARG1=>b, RESULT=>a+b] +""") + +p("""Where a and b are all possible values which can be summed. +Clearly, it is not possible to enumerate this table, but given +a sequence of substitutions with defined values for ARG0 and ARG1 +such as""") + +pre(""" + [ARG0=>1, ARG1=-4] + [ARG0=>2.6, ARG1=50] + [ARG0=>99, ARG1=1] +""") + +p("""it is possible to implement a "join operation" against +this sequence that performs the same augmentation as a join with +the infinite table defined above:""") + +pre(""" + [ARG0=>1, ARG1=-4, RESULT=-3] + [ARG0=>2.6, ARG1=50, RESULT=52.6] + [ARG0=>99, ARG1=1, RESULT=100] +""") + +p("""Furthermore by giving an "infinite estimate" for +all attempts to evaluate the join where ARG0 and ARG1 are not +available the generalized table implementation for the addition +operation can refuse to compute an "infinite join." """) + +p("""More generally all functions f(a,b,c,d) are represented in +gadfly as generalized tables containing all possible relevant +entries""") + +pre(""" + [ARG0=>a, ARG1=>b, ARG2=>c, ARG3=>d, RESULT=>f(a,b,c,d)]""") + +p("""and the join estimation function refuses all attempts to perform +a join unless all the arguments are provided by the input substitution +sequence.""") + +header("Implementing Predicates") + +p("""Similarly to functions, predicates such as less-than and BETWEEN +and LIKE are implemented using the generalized table mechanism. +For example the "x BETWEEN y AND z" predicate is implemented +as a generalized table "containing" all possible""") + +pre(""" + [ARG0=>a, ARG1=>b, ARG2=>c]""") + +p("""where b<a<c. Furthermore joins with this table are not +permitted unless all three arguments are available in the sequence +of input substitutions.""") + +header("Some Gadfly extension interfaces") + +p("""A gadfly database engine may be extended with user defined +functions, predicates, and alternative table and index implementations. +This section snapshots several Gadfly 2.0 interfaces, currently under +development and likely to change before the package is released.""") + +p("""The basic interface for adding functions and predicates (logical tests) +to a gadfly engine are relatively straightforward. For example to add the +ability to match a regular expression within a gadfly query use the +following implementation.""") + +pre(""" + from re import match + + def addrematch(gadflyinstance): + gadflyinstance.add_predicate("rematch", match) +""") +p(""" +Then upon connecting to the database execute +""") +pre(""" + g = gadfly(...) + ... + addrematch(g) +""") +p(""" +In this case the "semijoin operation" associated with the new predicate +"rematch" is automatically generated, and after the add_predicate +binding operation the gadfly instance supports queries such as""") +pre(""" + select drinker, beer + from likes + where rematch('b*', beer) and drinker not in + (select drinker from frequents where rematch('c*', bar)) +""") +p(""" +By embedding the "rematch" operation within the query the SQL +engine can do "more work" for the programmer and reduce or eliminate the +need to process the query result externally to the engine. +""") +p(""" +In a similar manner functions may be added to a gadfly instance,""") +pre(""" + def modulo(x,y): + return x % y + + def addmodulo(gadflyinstance): + gadflyinstance.add_function("modulo", modulo) + + ... + g = gadfly(...) + ... + addmodulo(g) +""") +p(""" +Then after the binding the modulo function can be used whereever +an SQL expression can occur. +""") +p(""" +Adding alternative table implementations to a Gadfly instance +is more interesting and more difficult. An "extension table" implementation +must conform to the following interface:""") + +pre(""" + # get the kjbuckets.kjSet set of attribute names for this table + names = table.attributes() + + # estimate the difficulty of evaluating a join given known attributes + # return None for "impossible" or n>=0 otherwise with larger values + # indicating greater difficulty or expense + estimate = table.estimate(known_attributes) + + # return the join of the rows of the table with + # the list of kjbuckets.kjDict mappings as a list of mappings. + resultmappings = table.join(listofmappings) +""") +p(""" +In this case add the table to a gadfly instance using""") +pre(""" + gadflyinstance.add_table("table_name", table) +""") +p(""" +For example to add a table which automatically queries filenames +in the filesystems of the host computer a gadfly instance could +be augmented with a GLOB table implemented using the standard +library function glob.glob as follows:""") +pre(""" + import kjbuckets + + class GlobTable: + def __init__(self): pass + + def attributes(self): + return kjbuckets.kjSet("PATTERN", "NAME") + + def estimate(self, known_attributes): + if known_attributes.member("PATTERN"): + return 66 # join not too difficult + else: + return None # join is impossible (must have PATTERN) + + def join(self, listofmappings): + from glob import glob + result = [] + for m in listofmappings: + pattern = m["PATTERN"] + for name in glob(pattern): + newmapping = kjbuckets.kjDict(m) + newmapping["NAME"] = name + if newmapping.Clean(): + result.append(newmapping) + return result + + ... + gadfly_instance.add_table("GLOB", GlobTable()) +""") +p(""" +Then one could formulate queries such as "list the files in directories +associated with packages installed by guido" +""") +pre(""" + select g.name as filename + from packages p, glob g + where p.installer = 'guido' and g.pattern=p.root_directory +""") +p(""" +Note that conceptually the GLOB table is an infinite table including +all filenames on the current computer in the "NAME" column, paired with +a potentially infinite number of patterns. +""") +p(""" +More interesting examples would allow queries to remotely access +data served by an HTTP server, or from any other resource. +""") +p(""" +Furthermore an extension table can be augmented with update methods +""") +pre(""" + table.insert_rows(listofmappings) + table.update_rows(oldlist, newlist) + table.delete_rows(oldlist) +""") +p(""" +Note: at present the implementation does not enforce recovery or +transaction semantics for updates to extension tables, although this +may change in the final release. +""") +p(""" +The table implementation is free to provide its own implementations of +indices which take advantage of data provided by the join argument. +""") + +header("Efficiency Notes") + +p("""The following thought experiment attempts to explain why the +Gadfly implementation is surprisingly fast considering that it +is almost entirely implemented in Python (an interpreted programming +language which is not especially fast when compared to alternatives). +Although Gadfly is quite complex, at an abstract level the process +of query evaluation boils down to a series of embedded loops. +Consider the following nested loops:""") + +pre(""" + iterate 1000: + f(...) # fixed cost of outer loop + iterate 10: + g(...) # fixed cost of middle loop + iterate 10: + # the real work (string parse, matrix mul, query eval...) + h(...)""") + +p("""In my experience many computations follow this pattern where +f, g, are complex, dynamic, special purpose and h is simple, general +purpose, static. Some example computations that follow this pattern +include: file massaging (perl), matrix manipulation (python, tcl), +database/cgi page generation, and vector graphics/imaging.""") + +p("""Suppose implementing f, g, h in python is easy but result in +execution times10 times slower than a much harder implementation +in C, choosing arbitrary and debatable numbers assume each function +call consumes 1 tick in C, 5 ticks in java, 10 ticks in python +for a straightforward implementation of each function f, g, and +h. Under these conditions we get the following cost analysis, +eliminating some uninteresting combinations, of implementing the +function f, g, and h in combinations of Python, C and java:""") + +pre(""" +COST | FLANG | GLANG | HLANG +================================== +111000 | C | C | C +115000 | java | C | C +120000 | python | C | C +155000 | java | java | C +210000 | python | python | C +555000 | java | java | java +560000 | python | java | java +610000 | python | python | java +1110000 | python | python | python +""") + +p("""Note that moving only the innermost loop to C (python/python/C) +speeds up the calculation by half an order of magnitude compared +to the python-only implementation and brings the speed to within +a factor of 2 of an implementation done entirely in C.""") + +p("""Although this artificial and contrived thought experiment is +far from conclusive, we may be tempted to draw the conclusion +that generally programmers should focus first on obtaining a working +implementation (because as John Ousterhout is reported to have +said "the biggest performance improvement is the transition +from non-working to working") using the methodology that +is most likely to obtain a working solution the quickest (Python). Only then if the performance +is inadequate should the programmer focus on optimizing +the inner most loops, perhaps moving them to a very efficient +implementation (C). Optimizing the outer loops will buy little +improvement, and should be done later, if ever.""") + +p("""This was precisely the strategy behind the gadfly implementations, +where most of the inner loops are implemented in the kjbuckets +C extension module and the higher level logic is all in Python. +This also explains why gadfly appears to be "slower" +for simple queries over small data sets, but seems to be relatively +"faster" for more complex queries over larger data sets, +since larger queries and data sets take better advantage of the +optimized inner loops.""") + +header("A Gadfly variant for OLAP?") + +p("""In private correspondence Andy Robinson points out that the +basic logical design underlying Gadfly could be adapted to provide +Online Analytical Processing (OLAP) and other forms of data warehousing +and data mining. Since SQL is not particularly well suited for +the kinds of requests common in these domains the higher level +interfaces would require modification, but the underlying logic +of substitutions and name mappings seems to be appropriate.""") + +header("Conclusion") + +p("""The revamped query engine design in Gadfly 2 supports +a flexible and general extension methodology that permits programmers +to extend the gadfly engine to include additional computations +and access to remote data sources. Among other possibilities this +will permit the gadfly engine to make use of disk based indexed +tables and to dynamically retrieve information from remote data +sources (such as an Excel spreadsheet or an Oracle database). +These features will make gadfly a very useful tool for data manipulation +and integration.""") + +header("References") + +p("""[Van Rossum] Van Rossum, Python Reference Manual, Tutorial, and Library Manuals, +please look to http://www.python.org +for the latest versions, downloads and links to printed versions.""") + +p("""[O'Neill] O'Neill, P., Data Base Principles, Programming, Performance, +Morgan Kaufmann Publishers, San Francisco, 1994.""") + +p("""[Korth and Silberschatz] Korth, H. and Silberschatz, A. and Sudarshan, S. +Data Base System Concepts, McGraw-Hill Series in Computer Science, Boston, +1997""") + +p("""[Gadfly]Gadfly: SQL Relational Database in Python, +http://www.chordate.com/kwParsing/gadfly.html""") + +go() \ No newline at end of file diff --git a/bin/reportlab/demos/odyssey/00readme.txt b/bin/reportlab/demos/odyssey/00readme.txt new file mode 100644 index 00000000000..95c622ac279 --- /dev/null +++ b/bin/reportlab/demos/odyssey/00readme.txt @@ -0,0 +1,56 @@ +This contains a number of benchmarks and demos +based on Homer's Odyssey (which is widely available +in plain, line-oriented text format). There are a large +selection of online books at: + http://classics.mit.edu/ + + +Our distribution ships with just the first chapter +in odyssey.txt. For a more meaningful speed test, +download the full copy from + http://www.reportlab.com/ftp/odyssey.full.zip +or + ftp://ftp.reportlab.com/odyssey.full.zip +and unzip to extract odyssey.full.txt (608kb). + +Benchmark speed depends quite critically +on the presence of our accelerator module, +_rl_accel, which is a C (or Java) extension. +Serious users ought to compile or download this! + +The times quoted are from one machine (Andy Robinson's +home PC, approx 1.2Ghz 128Mb Ram, Win2k in Sep 2003) +in order to give a rough idea of what features cost +what performance. + + +The tests are as follows: + +(1) odyssey.py (produces odyssey.pdf) +This demo takes a large volume of text and prints it +in the simplest way possible. It is a demo of the +basic technique of looping down a page manually and +breaking at the bottom. On my 1.2 Ghz machine this takes +1.91 seconds (124 pages per second) + +(2) fodyssey.py (produces fodyssey.pdf) +This is a 'flowing document' we parse the file and +throw away line breaks to make proper paragraphs. +The Platypus framework renders these. This necessitates +measuring the width of every word in every paragraph +for wrapping purposes. + +This takes 3.27 seconds on the same machine. Paragraph +wrapping basically doubles the work. The text is more +compact with about 50% more words per page. Very roughly, +we can wrap 40 pages of ten-point text per second and save +to PDF. + +(3) dodyssey.py (produced dodyssey.pdf) +This is a slightly fancier version which uses different +page templates (one column for first page in a chapter, +two column for body poages). The additional layout logic +adds about 15%, going up to 3.8 seconds. This is probably +a realistic benchmark for a simple long text document +with a single pass. Documents doing cross-references +and a table of contents might need twice as long. diff --git a/bin/reportlab/demos/odyssey/dodyssey.py b/bin/reportlab/demos/odyssey/dodyssey.py new file mode 100644 index 00000000000..edc24354eca --- /dev/null +++ b/bin/reportlab/demos/odyssey/dodyssey.py @@ -0,0 +1,254 @@ +#Copyright ReportLab Europe Ltd. 2000-2004 +#see license.txt for license details +#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/odyssey/dodyssey.py +__version__=''' $Id: dodyssey.py 2856 2006-05-11 09:48:13Z rgbecker $ ''' +__doc__='' + +#REPORTLAB_TEST_SCRIPT +import sys, copy, string, os +from reportlab.platypus import * +_NEW_PARA=os.environ.get('NEW_PARA','0')[0] in ('y','Y','1') +_REDCAP=int(os.environ.get('REDCAP','0')) +_CALLBACK=os.environ.get('CALLBACK','0')[0] in ('y','Y','1') +if _NEW_PARA: + def Paragraph(s,style): + from rlextra.radxml.para import Paragraph as PPPP + return PPPP(s,style) + +from reportlab.lib.units import inch +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY + +import reportlab.rl_config +reportlab.rl_config.invariant = 1 + +styles = getSampleStyleSheet() + +Title = "The Odyssey" +Author = "Homer" + +def myTitlePage(canvas, doc): + canvas.saveState() + canvas.restoreState() + +def myLaterPages(canvas, doc): + canvas.saveState() + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "Page %d" % doc.page) + canvas.restoreState() + +def go(): + def myCanvasMaker(fn,**kw): + from reportlab.pdfgen.canvas import Canvas + canv = apply(Canvas,(fn,),kw) + # attach our callback to the canvas + canv.myOnDrawCB = myOnDrawCB + return canv + + doc = BaseDocTemplate('dodyssey.pdf',showBoundary=0) + + #normal frame as for SimpleFlowDocument + frameT = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id='normal') + + #Two Columns + frame1 = Frame(doc.leftMargin, doc.bottomMargin, doc.width/2-6, doc.height, id='col1') + frame2 = Frame(doc.leftMargin+doc.width/2+6, doc.bottomMargin, doc.width/2-6, + doc.height, id='col2') + doc.addPageTemplates([PageTemplate(id='First',frames=frameT, onPage=myTitlePage), + PageTemplate(id='OneCol',frames=frameT, onPage=myLaterPages), + PageTemplate(id='TwoCol',frames=[frame1,frame2], onPage=myLaterPages), + ]) + doc.build(Elements,canvasmaker=myCanvasMaker) + +Elements = [] + +ChapterStyle = copy.deepcopy(styles["Heading1"]) +ChapterStyle.alignment = TA_CENTER +ChapterStyle.fontsize = 14 +InitialStyle = copy.deepcopy(ChapterStyle) +InitialStyle.fontsize = 16 +InitialStyle.leading = 20 +PreStyle = styles["Code"] + +def newPage(): + Elements.append(PageBreak()) + +chNum = 0 +def myOnDrawCB(canv,kind,label): + print 'myOnDrawCB(%s)'%kind, 'Page number=', canv.getPageNumber(), 'label value=', label + +def chapter(txt, style=ChapterStyle): + global chNum + Elements.append(NextPageTemplate('OneCol')) + newPage() + chNum = chNum + 1 + if _NEW_PARA or not _CALLBACK: + Elements.append(Paragraph(('chap %d'%chNum)+txt, style)) + else: + Elements.append(Paragraph(('foo '%chNum)+txt, style)) + Elements.append(Spacer(0.2*inch, 0.3*inch)) + if useTwoCol: + Elements.append(NextPageTemplate('TwoCol')) + +def fTitle(txt,style=InitialStyle): + Elements.append(Paragraph(txt, style)) + +ParaStyle = copy.deepcopy(styles["Normal"]) +ParaStyle.spaceBefore = 0.1*inch +if 'right' in sys.argv: + ParaStyle.alignment = TA_RIGHT +elif 'left' in sys.argv: + ParaStyle.alignment = TA_LEFT +elif 'justify' in sys.argv: + ParaStyle.alignment = TA_JUSTIFY +elif 'center' in sys.argv or 'centre' in sys.argv: + ParaStyle.alignment = TA_CENTER +else: + ParaStyle.alignment = TA_JUSTIFY + +useTwoCol = 'notwocol' not in sys.argv + +def spacer(inches): + Elements.append(Spacer(0.1*inch, inches*inch)) + +def p(txt, style=ParaStyle): + if _REDCAP: + fs, fe = '', '' + n = len(txt) + for i in xrange(n): + if 'a'<=txt[i]<='z' or 'A'<=txt[i]<='Z': + txt = (txt[:i]+(fs+txt[i]+fe))+txt[i+1:] + break + if _REDCAP>=2 and n>20: + j = i+len(fs)+len(fe)+1+int((n-1)/2) + while not ('a'<=txt[j]<='z' or 'A'<=txt[j]<='Z'): j += 1 + txt = (txt[:j]+(''+txt[j]+''))+txt[j+1:] + + if _REDCAP==3 and n>20: + n = len(txt) + fs = '' + for i in xrange(n-1,-1,-1): + if 'a'<=txt[i]<='z' or 'A'<=txt[i]<='Z': + txt = txt[:i]+((fs+txt[i]+fe)+txt[i+1:]) + break + + Elements.append(Paragraph(txt, style)) + +firstPre = 1 +def pre(txt, style=PreStyle): + global firstPre + if firstPre: + Elements.append(NextPageTemplate('OneCol')) + newPage() + firstPre = 0 + + spacer(0.1) + p = Preformatted(txt, style) + Elements.append(p) + +def parseOdyssey(fn): + from time import time + E = [] + t0=time() + L = open(fn,'r').readlines() + t1 = time() + print "open(%s,'r').readlines() took %.4f seconds" %(fn,t1-t0) + for i in xrange(len(L)): + if L[i][-1]=='\012': + L[i] = L[i][:-1] + t2 = time() + print "Removing all linefeeds took %.4f seconds" %(t2-t1) + L.append('') + L.append('-----') + + def findNext(L, i): + while 1: + if string.strip(L[i])=='': + del L[i] + kind = 1 + if i%s' % Title, InitialStyle]) + E.append([fTitle,'by %s' % Author, InitialStyle]) + + while 1: + if f>=len(L): break + + if string.upper(L[f][0:5])=='BOOK ': + E.append([chapter,L[f]]) + f=f+1 + while string.strip(L[f])=='': del L[f] + style = ParaStyle + func = p + else: + style = PreStyle + func = pre + + while 1: + s=f + f, k=findNext(L,s) + sep= (func is pre) and '\012' or ' ' + E.append([func,string.join(L[s:f],sep),style]) + if k: break + t3 = time() + print "Parsing into memory took %.4f seconds" %(t3-t2) + del L + t4 = time() + print "Deleting list of lines took %.4f seconds" %(t4-t3) + for i in xrange(len(E)): + apply(E[i][0],E[i][1:]) + t5 = time() + print "Moving into platypus took %.4f seconds" %(t5-t4) + del E + t6 = time() + print "Deleting list of actions took %.4f seconds" %(t6-t5) + go() + t7 = time() + print "saving to PDF took %.4f seconds" %(t7-t6) + print "Total run took %.4f seconds"%(t7-t0) + + import md5 + print 'file digest: %s' % md5.md5(open('dodyssey.pdf','rb').read()).hexdigest() + +def run(): + for fn in ('odyssey.full.txt','odyssey.txt'): + if os.path.isfile(fn): + parseOdyssey(fn) + break + +def doProf(profname,func,*args,**kwd): + import hotshot, hotshot.stats + prof = hotshot.Profile(profname) + prof.runcall(func) + prof.close() + stats = hotshot.stats.load(profname) + stats.strip_dirs() + stats.sort_stats('time', 'calls') + stats.print_stats(20) + +if __name__=='__main__': + if '--prof' in sys.argv: + doProf('dodyssey.prof',run) + else: + run() diff --git a/bin/reportlab/demos/odyssey/fodyssey.py b/bin/reportlab/demos/odyssey/fodyssey.py new file mode 100644 index 00000000000..bbb6797e7a7 --- /dev/null +++ b/bin/reportlab/demos/odyssey/fodyssey.py @@ -0,0 +1,165 @@ +#Copyright ReportLab Europe Ltd. 2000-2004 +#see license.txt for license details +#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/odyssey/fodyssey.py +__version__=''' $Id: fodyssey.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' +__doc__='' + +#REPORTLAB_TEST_SCRIPT +import sys, copy, string, os +from reportlab.platypus import * +from reportlab.lib.units import inch +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY + +styles = getSampleStyleSheet() + +Title = "The Odyssey" +Author = "Homer" + +def myFirstPage(canvas, doc): + canvas.saveState() + canvas.restoreState() + +def myLaterPages(canvas, doc): + canvas.saveState() + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "Page %d" % doc.page) + canvas.restoreState() + +def go(): + doc = SimpleDocTemplate('fodyssey.pdf',showBoundary='showboundary' in sys.argv) + doc.allowSplitting = not 'nosplitting' in sys.argv + doc.build(Elements,myFirstPage,myLaterPages) + +Elements = [] + +ChapterStyle = copy.copy(styles["Heading1"]) +ChapterStyle.alignment = TA_CENTER +ChapterStyle.fontsize = 16 +InitialStyle = copy.deepcopy(ChapterStyle) +InitialStyle.fontsize = 16 +InitialStyle.leading = 20 +PreStyle = styles["Code"] + +def newPage(): + Elements.append(PageBreak()) + +def chapter(txt, style=ChapterStyle): + newPage() + Elements.append(Paragraph(txt, style)) + Elements.append(Spacer(0.2*inch, 0.3*inch)) + +def fTitle(txt,style=InitialStyle): + Elements.append(Paragraph(txt, style)) + +ParaStyle = copy.deepcopy(styles["Normal"]) +ParaStyle.spaceBefore = 0.1*inch +if 'right' in sys.argv: + ParaStyle.alignment = TA_RIGHT +elif 'left' in sys.argv: + ParaStyle.alignment = TA_LEFT +elif 'justify' in sys.argv: + ParaStyle.alignment = TA_JUSTIFY +elif 'center' in sys.argv or 'centre' in sys.argv: + ParaStyle.alignment = TA_CENTER +else: + ParaStyle.alignment = TA_JUSTIFY + +def spacer(inches): + Elements.append(Spacer(0.1*inch, inches*inch)) + +def p(txt, style=ParaStyle): + Elements.append(Paragraph(txt, style)) + +def pre(txt, style=PreStyle): + spacer(0.1) + p = Preformatted(txt, style) + Elements.append(p) + +def parseOdyssey(fn): + from time import time + E = [] + t0=time() + L = open(fn,'r').readlines() + t1 = time() + print "open(%s,'r').readlines() took %.4f seconds" %(fn,t1-t0) + for i in xrange(len(L)): + if L[i][-1]=='\012': + L[i] = L[i][:-1] + t2 = time() + print "Removing all linefeeds took %.4f seconds" %(t2-t1) + L.append('') + L.append('-----') + + def findNext(L, i): + while 1: + if string.strip(L[i])=='': + del L[i] + kind = 1 + if i%s' % Title, InitialStyle]) + E.append([fTitle,'by %s' % Author, InitialStyle]) + + while 1: + if f>=len(L): break + + if string.upper(L[f][0:5])=='BOOK ': + E.append([chapter,L[f]]) + f=f+1 + while string.strip(L[f])=='': del L[f] + style = ParaStyle + func = p + else: + style = PreStyle + func = pre + + while 1: + s=f + f, k=findNext(L,s) + sep= (func is pre) and '\012' or ' ' + E.append([func,string.join(L[s:f],sep),style]) + if k: break + t3 = time() + print "Parsing into memory took %.4f seconds" %(t3-t2) + del L + t4 = time() + print "Deleting list of lines took %.4f seconds" %(t4-t3) + for i in xrange(len(E)): + apply(E[i][0],E[i][1:]) + t5 = time() + print "Moving into platypus took %.4f seconds" %(t5-t4) + del E + t6 = time() + print "Deleting list of actions took %.4f seconds" %(t6-t5) + go() + t7 = time() + print "saving to PDF took %.4f seconds" %(t7-t6) + print "Total run took %.4f seconds"%(t7-t0) + +for fn in ('odyssey.full.txt','odyssey.txt'): + if os.path.isfile(fn): + break +if __name__=='__main__': + parseOdyssey(fn) \ No newline at end of file diff --git a/bin/reportlab/demos/odyssey/odyssey.py b/bin/reportlab/demos/odyssey/odyssey.py new file mode 100644 index 00000000000..ae8d9d6188c --- /dev/null +++ b/bin/reportlab/demos/odyssey/odyssey.py @@ -0,0 +1,151 @@ +#Copyright ReportLab Europe Ltd. 2000-2004 +#see license.txt for license details +#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/odyssey/odyssey.py +__version__=''' $Id: odyssey.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' +___doc__='' +#odyssey.py +# +#Demo/benchmark of PDFgen rendering Homer's Odyssey. + + + +#results on my humble P266 with 64MB: +# Without page compression: +# 239 pages in 3.76 seconds = 77 pages per second + +# With textOut rather than textLine, i.e. computing width +# of every word as we would for wrapping: +# 239 pages in 10.83 seconds = 22 pages per second + +# With page compression and textLine(): +# 239 pages in 39.39 seconds = 6 pages per second + +from reportlab.pdfgen import canvas +import time, os, sys + +#find out what platform we are on and whether accelerator is +#present, in order to print this as part of benchmark info. +try: + import _rl_accel + ACCEL = 1 +except ImportError: + ACCEL = 0 + + + + +from reportlab.lib.units import inch, cm +from reportlab.lib.pagesizes import A4 + +#precalculate some basics +top_margin = A4[1] - inch +bottom_margin = inch +left_margin = inch +right_margin = A4[0] - inch +frame_width = right_margin - left_margin + + +def drawPageFrame(canv): + canv.line(left_margin, top_margin, right_margin, top_margin) + canv.setFont('Times-Italic',12) + canv.drawString(left_margin, top_margin + 2, "Homer's Odyssey") + canv.line(left_margin, top_margin, right_margin, top_margin) + + + canv.line(left_margin, bottom_margin, right_margin, bottom_margin) + canv.drawCentredString(0.5*A4[0], 0.5 * inch, + "Page %d" % canv.getPageNumber()) + + + +def run(verbose=1): + if sys.platform[0:4] == 'java': + impl = 'Jython' + else: + impl = 'Python' + verStr = '%d.%d' % (sys.version_info[0:2]) + if ACCEL: + accelStr = 'with _rl_accel' + else: + accelStr = 'without _rl_accel' + print 'Benchmark of %s %s %s' % (impl, verStr, accelStr) + + started = time.time() + canv = canvas.Canvas('odyssey.pdf', invariant=1) + canv.setPageCompression(1) + drawPageFrame(canv) + + #do some title page stuff + canv.setFont("Times-Bold", 36) + canv.drawCentredString(0.5 * A4[0], 7 * inch, "Homer's Odyssey") + + canv.setFont("Times-Bold", 18) + canv.drawCentredString(0.5 * A4[0], 5 * inch, "Translated by Samuel Burton") + + canv.setFont("Times-Bold", 12) + tx = canv.beginText(left_margin, 3 * inch) + tx.textLine("This is a demo-cum-benchmark for PDFgen. It renders the complete text of Homer's Odyssey") + tx.textLine("from a text file. On my humble P266, it does 77 pages per secondwhile creating a 238 page") + tx.textLine("document. If it is asked to computer text metrics, measuring the width of each word as ") + tx.textLine("one would for paragraph wrapping, it still manages 22 pages per second.") + tx.textLine("") + tx.textLine("Andy Robinson, Robinson Analytics Ltd.") + canv.drawText(tx) + + canv.showPage() + #on with the text... + drawPageFrame(canv) + + canv.setFont('Times-Roman', 12) + tx = canv.beginText(left_margin, top_margin - 0.5*inch) + + for fn in ('odyssey.full.txt','odyssey.txt'): + if os.path.isfile(fn): + break + + data = open(fn,'r').readlines() + for line in data: + #this just does it the fast way... + tx.textLine(line) + #this forces it to do text metrics, which would be the slow + #part if we were wrappng paragraphs. + #canv.textOut(line) + #canv.textLine('') + + #page breaking + y = tx.getY() #get y coordinate + if y < bottom_margin + 0.5*inch: + canv.drawText(tx) + canv.showPage() + drawPageFrame(canv) + canv.setFont('Times-Roman', 12) + tx = canv.beginText(left_margin, top_margin - 0.5*inch) + + #page + pg = canv.getPageNumber() + if verbose and pg % 10 == 0: + print 'formatted page %d' % canv.getPageNumber() + + if tx: + canv.drawText(tx) + canv.showPage() + drawPageFrame(canv) + + if verbose: + print 'about to write to disk...' + + canv.save() + + finished = time.time() + elapsed = finished - started + pages = canv.getPageNumber()-1 + speed = pages / elapsed + fileSize = os.stat('odyssey.pdf')[6] / 1024 + print '%d pages in %0.2f seconds = %0.2f pages per second, file size %d kb' % ( + pages, elapsed, speed, fileSize) + import md5 + print 'file digest: %s' % md5.md5(open('odyssey.pdf','rb').read()).hexdigest() + +if __name__=='__main__': + quiet = ('-q' in sys.argv) + run(verbose = not quiet) \ No newline at end of file diff --git a/bin/reportlab/demos/odyssey/odyssey.txt b/bin/reportlab/demos/odyssey/odyssey.txt new file mode 100644 index 00000000000..f5e4b7f9d60 --- /dev/null +++ b/bin/reportlab/demos/odyssey/odyssey.txt @@ -0,0 +1,207 @@ +Provided by The Internet Classics Archive. +See bottom for copyright. Available online at + http://classics.mit.edu//Homer/odyssey.html + +The Odyssey +By Homer + + +Translated by Samuel Butler + +---------------------------------------------------------------------- + +BOOK I +ITell me, O muse, of that ingenious hero who travelled far and wide + a b c &| & | A' A ' after he had sacked the famous town of Troy. Many cities did he visit, +and many were the nations with whose manners and customs he was acquainted; +moreover he suffered much by sea while trying to save his own life +and bring his men safely home; but do what he might he could not +save1 +his men, for they perished through their own sheer folly in eating +the cattle of the Sun-god Hyperion; so the god prevented them from +ever reaching home. Tell me, too, about all these things, O daughter +of Jove, from whatsoever source you may know them. + +So now all who escaped death in battle or by shipwreck had got safely +home except Ulysses, and he, though he was longing to return to his +wife and country, was detained by the goddess Calypso, who had got +him into a large cave and wanted to marry him. But as years went by, +there came a time when the gods settled that he should go back to +Ithaca; even then, however, when he was among his own people, his +troubles were not yet over; nevertheless all the gods had now begun +to pity him except Neptune, who still persecuted him without ceasing +and would not let him get home. + +Now Neptune had gone off to the Ethiopians, who are at the world's +end, and lie in two halves, the one looking West and the other East. +He had gone there to accept a hecatomb of sheep and oxen, and was +enjoying himself at his festival; but the other gods met in the house +of Olympian Jove, and the sire of gods and men spoke first. At that +moment he was thinking of Aegisthus, who had been killed by Agamemnon's +son Orestes; so he said to the other gods: + +"See now, how men lay blame upon us gods for what is after all nothing +but their own folly. Look at Aegisthus; he must needs make love to +Agamemnon's wife unrighteously and then kill Agamemnon, though he +knew it would be the death of him; for I sent Mercury to warn him +not to do either of these things, inasmuch as Orestes would be sure +to take his revenge when he grew up and wanted to return home. Mercury +told him this in all good will but he would not listen, and now he +has paid for everything in full." + +Then Minerva said, "Father, son of Saturn, King of kings, it served +Aegisthus right, and so it would any one else who does as he did; +but Aegisthus is neither here nor there; it is for Ulysses that my +heart bleeds, when I think of his sufferings in that lonely sea-girt +island, far away, poor man, from all his friends. It is an island +covered with forest, in the very middle of the sea, and a goddess +lives there, daughter of the magician Atlas, who looks after the bottom +of the ocean, and carries the great columns that keep heaven and earth +asunder. This daughter of Atlas has got hold of poor unhappy Ulysses, +and keeps trying by every kind of blandishment to make him forget +his home, so that he is tired of life, and thinks of nothing but how +he may once more see the smoke of his own chimneys. You, sir, take +no heed of this, and yet when Ulysses was before Troy did he not propitiate +you with many a burnt sacrifice? Why then should you keep on being +so angry with him?" + +And Jove said, "My child, what are you talking about? How can I forget +Ulysses than whom there is no more capable man on earth, nor more +liberal in his offerings to the immortal gods that live in heaven? +Bear in mind, however, that Neptune is still furious with Ulysses +for having blinded an eye of Polyphemus king of the Cyclopes. Polyphemus +is son to Neptune by the nymph Thoosa, daughter to the sea-king Phorcys; +therefore though he will not kill Ulysses outright, he torments him +by preventing him from getting home. Still, let us lay our heads together +and see how we can help him to return; Neptune will then be pacified, +for if we are all of a mind he can hardly stand out against us." + +And Minerva said, "Father, son of Saturn, King of kings, if, then, +the gods now mean that Ulysses should get home, we should first send +Mercury to the Ogygian island to tell Calypso that we have made up +our minds and that he is to return. In the meantime I will go to Ithaca, +to put heart into Ulysses' son Telemachus; I will embolden him to +call the Achaeans in assembly, and speak out to the suitors of his +mother Penelope, who persist in eating up any number of his sheep +and oxen; I will also conduct him to Sparta and to Pylos, to see if +he can hear anything about the return of his dear father- for this +will make people speak well of him." + +Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. +Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. +Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. +Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. +Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. +Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. +Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. +Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. +Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. + +"Men of Ithaca, it is all your own fault that things have turned out +as they have; you would not listen to me, nor yet to Mentor, when +we bade you check the folly of your sons who were doing much wrong +in the wantonness of their hearts- wasting the substance and dishonouring +the wife of a chieftain who they thought would not return. Now, however, +let it be as I say, and do as I tell you. Do not go out against Ulysses, +or you may find that you have been drawing down evil on your own heads." + +This was what he said, and more than half raised a loud shout, and +at once left the assembly. But the rest stayed where they were, for +the speech of Halitherses displeased them, and they sided with Eupeithes; +they therefore hurried off for their armour, and when they had armed +themselves, they met together in front of the city, and Eupeithes +led them on in their folly. He thought he was going to avenge the +murder of his son, whereas in truth he was never to return, but was +himself to perish in his attempt. + +Then Minerva said to Jove, "Father, son of Saturn, king of kings, +answer me this question- What do you propose to do? Will you set them +fighting still further, or will you make peace between them?" + +And Jove answered, "My child, why should you ask me? Was it not by +your own arrangement that Ulysses came home and took his revenge upon +the suitors? Do whatever you like, but I will tell you what I think +will be most reasonable arrangement. Now that Ulysses is revenged, +let them swear to a solemn covenant, in virtue of which he shall continue +to rule, while we cause the others to forgive and forget the massacre +of their sons and brothers. Let them then all become friends as heretofore, +and let peace and plenty reign." + +This was what Minerva was already eager to bring about, so down she +darted from off the topmost summits of Olympus. + +Now when Laertes and the others had done dinner, Ulysses began by +saying, "Some of you go out and see if they are not getting close +up to us." So one of Dolius's sons went as he was bid. Standing on +the threshold he could see them all quite near, and said to Ulysses, +"Here they are, let us put on our armour at once." + +They put on their armour as fast as they could- that is to say Ulysses, +his three men, and the six sons of Dolius. Laertes also and Dolius +did the same- warriors by necessity in spite of their grey hair. When +they had all put on their armour, they opened the gate and sallied +forth, Ulysses leading the way. + +Then Jove's daughter Minerva came up to them, having assumed the form +and voice of Mentor. Ulysses was glad when he saw her, and said to +his son Telemachus, "Telemachus, now that are about to fight in an +engagement, which will show every man's mettle, be sure not to disgrace +your ancestors, who were eminent for their strength and courage all +the world over." + +"You say truly, my dear father," answered Telemachus, "and you shall +see, if you will, that I am in no mind to disgrace your family." + +Laertes was delighted when he heard this. "Good heavens, he exclaimed, +"what a day I am enjoying: I do indeed rejoice at it. My son and grandson +are vying with one another in the matter of valour." + +On this Minerva came close up to him and said, "Son of Arceisius- +best friend I have in the world- pray to the blue-eyed damsel, and +to Jove her father; then poise your spear and hurl it." + +As she spoke she infused fresh vigour into him, and when he had prayed +to her he poised his spear and hurled it. He hit Eupeithes' helmet, +and the spear went right through it, for the helmet stayed it not, +and his armour rang rattling round him as he fell heavily to the ground. +Meantime Ulysses and his son fell the front line of the foe and smote +them with their swords and spears; indeed, they would have killed +every one of them, and prevented them from ever getting home again, +only Minerva raised her voice aloud, and made every one pause. "Men +of Ithaca," she cried, cease this dreadful war, and settle the matter +at once without further bloodshed." + +On this pale fear seized every one; they were so frightened that their +arms dropped from their hands and fell upon the ground at the sound +of the goddess's voice, and they fled back to the city for their lives. +But Ulysses gave a great cry, and gathering himself together swooped +down like a soaring eagle. Then the son of Saturn sent a thunderbolt +of fire that fell just in front of Minerva, so she said to Ulysses, +"Ulysses, noble son of Laertes, stop this warful strife, or Jove will +be angry with you." + +Thus spoke Minerva, and Ulysses obeyed her gladly. Then Minerva assumed +the form and voice of Mentor, and presently made a covenant of peace +between the two contending parties. + +THE END + +---------------------------------------------------------------------- + +Copyright statement: +The Internet Classics Archive by Daniel C. Stevenson, Web Atomics. +World Wide Web presentation is copyright (C) 1994-1998, Daniel +C. Stevenson, Web Atomics. +All rights reserved under international and pan-American copyright +conventions, including the right of reproduction in whole or in part +in any form. Direct permission requests to classics@classics.mit.edu. +Translation of "The Deeds of the Divine Augustus" by Augustus is +copyright (C) Thomas Bushnell, BSG. + + +To really test that reportlab can produce pages quickly download the +complete version of the test from http://classics.mit.edu//Homer/odyssey.html +and copy it to this directory as odyssey.full.txt. + +A zipped version of the full text is available for download at +ftp://ftp.reportlab.com/odyssey.full.zip diff --git a/bin/reportlab/demos/rlzope/readme.txt b/bin/reportlab/demos/rlzope/readme.txt new file mode 100644 index 00000000000..4bd60a5bb64 --- /dev/null +++ b/bin/reportlab/demos/rlzope/readme.txt @@ -0,0 +1,73 @@ +# rlzope : an external Zope method to show people how to use +# the ReportLab toolkit from within Zope. +# +# this method searches an image named 'logo' in the +# ZODB then prints it at the top of a simple PDF +# document made with ReportLab +# +# the resulting PDF document is returned to the +# user's web browser and, if possible, it is +# simultaneously saved into the ZODB. +# +# this method illustrates how to use both the platypus +# and canvas frameworks. +# +# License : The ReportLab Toolkit's license (similar to BSD) +# +# Author : Jerome Alet - alet@unice.fr +# + +Installation instructions : +=========================== + + 0 - If not installed then install Zope. + + 1 - Install reportlab in the Zope/lib/python/Shared directory by unpacking + the tarball and putting a reportlabs.pth file in site-packages for the Zope + used with Python. The path value in the reportlabs.pth file must be + relative. For a typical Zope installation, the path is "../../python/Shared". + Remember to restart Zope so the new path is instantiated. + + 2 - Install PIL in the Zope/lib/python/Shared directory. You need to + ensure that the _imaging.so or .pyd is also installed appropriately. + It should be compatible with the python running the zope site. + + 3 - Copy rlzope.py to your Zope installation's "Extensions" + subdirectory, e.g. /var/lib/zope/Extensions/ under Debian GNU/Linux. + + 4 - From within Zope's management interface, add an External Method with + these parameters : + + Id : rlzope + Title : rlzope + Module Name : rlzope + Function Name : rlzope + + 5 - From within Zope's management interface, add an image called "logo" + in the same Folder than rlzope, or somewhere above in the Folder + hierarchy. For example you can use ReportLab's logo which you + can find in reportlab/docs/images/replogo.gif + + 6 - Point your web browser to rlzope, e.g. on my laptop under + Debian GNU/Linux : + + http://localhost:9673/rlzope + + This will send a simple PDF document named 'dummy.pdf' to your + web browser, and if possible save it as a File object in the + Zope Object DataBase, with this name. Note, however, that if + an object with the same name already exists then it won't + be replaced for security reasons. + + You can optionally add a parameter called 'name' with + a filename as the value, to specify another filename, + e.g. : +logo + http://localhost:9673/rlzope?name=sample.pdf + + 7 - Adapt it to your own needs. + + 8 - Enjoy ! + +Send comments or bug reports at : alet@unice.fr + diff --git a/bin/reportlab/demos/rlzope/rlzope.py b/bin/reportlab/demos/rlzope/rlzope.py new file mode 100644 index 00000000000..867d8466759 --- /dev/null +++ b/bin/reportlab/demos/rlzope/rlzope.py @@ -0,0 +1,169 @@ +# +# Using the ReportLab toolkit from within Zope +# +# WARNING : The MyPDFDoc class deals with ReportLab's platypus framework, +# while the MyPageTemplate class directly deals with ReportLab's +# canvas, this way you know how to do with both... +# +# License : the ReportLab Toolkit's one +# see : http://www.reportlab.com +# +# Author : Jerome Alet - alet@unice.fr +# +# + +import string, cStringIO +try : + from Shared.reportlab.platypus.paragraph import Paragraph + from Shared.reportlab.platypus.doctemplate import * + from Shared.reportlab.lib.units import inch + from Shared.reportlab.lib import styles + from Shared.reportlab.lib.utils import ImageReader +except ImportError : + from reportlab.platypus.paragraph import Paragraph + from reportlab.platypus.doctemplate import * + from reportlab.lib.units import inch + from reportlab.lib import styles + from reportlab.lib.utils import ImageReader + +class MyPDFDoc : + class MyPageTemplate(PageTemplate) : + """Our own page template.""" + def __init__(self, parent) : + """Initialise our page template.""" + # + # we must save a pointer to our parent somewhere + self.parent = parent + + # Our doc is made of a single frame + content = Frame(0.75 * inch, 0.5 * inch, parent.document.pagesize[0] - 1.25 * inch, parent.document.pagesize[1] - (1.5 * inch)) + PageTemplate.__init__(self, "MyTemplate", [content]) + + # get all the images we need now, in case we've got + # several pages this will save some CPU + self.logo = self.getImageFromZODB("logo") + + def getImageFromZODB(self, name) : + """Retrieves an Image from the ZODB, converts it to PIL, + and makes it 0.75 inch high. + """ + try : + # try to get it from ZODB + logo = getattr(self.parent.context, name) + except AttributeError : + # not found ! + return None + + # Convert it to PIL + image = ImageReader(cStringIO.StringIO(str(logo.data))) + (width, height) = image.getSize() + + # scale it to be 0.75 inch high + multi = ((height + 0.0) / (0.75 * inch)) + width = int(width / multi) + height = int(height / multi) + + return ((width, height), image) + + def beforeDrawPage(self, canvas, doc) : + """Draws a logo and an contribution message on each page.""" + canvas.saveState() + if self.logo is not None : + # draws the logo if it exists + ((width, height), image) = self.logo + canvas.drawImage(image, inch, doc.pagesize[1] - inch, width, height) + canvas.setFont('Times-Roman', 10) + canvas.drawCentredString(inch + (doc.pagesize[0] - (1.5 * inch)) / 2, 0.25 * inch, "Contributed by Jerome Alet - alet@unice.fr") + canvas.restoreState() + + def __init__(self, context, filename) : + # save some datas + self.context = context + self.built = 0 + self.objects = [] + + # we will build an in-memory document + # instead of creating an on-disk file. + self.report = cStringIO.StringIO() + + # initialise a PDF document using ReportLab's platypus + self.document = BaseDocTemplate(self.report) + + # add our page template + # (we could add more than one, but I prefer to keep it simple) + self.document.addPageTemplates(self.MyPageTemplate(self)) + + # get the default style sheets + self.StyleSheet = styles.getSampleStyleSheet() + + # then build a simple doc with ReportLab's platypus + sometext = "A sample script to show how to use ReportLab from within Zope" + url = self.escapexml(context.absolute_url()) + urlfilename = self.escapexml(context.absolute_url() + '/%s' % filename) + self.append(Paragraph("Using ReportLab from within Zope", self.StyleSheet["Heading3"])) + self.append(Spacer(0, 10)) + self.append(Paragraph("You launched it from : %s" % url, self.StyleSheet['Normal'])) + self.append(Spacer(0, 40)) + self.append(Paragraph("If possible, this report will be automatically saved as : %s" % urlfilename, self.StyleSheet['Normal'])) + + # generation du document PDF + self.document.build(self.objects) + self.built = 1 + + def __str__(self) : + """Returns the PDF document as a string of text, or None if it's not ready yet.""" + if self.built : + return self.report.getvalue() + else : + return None + + def append(self, object) : + """Appends an object to our platypus "story" (using ReportLab's terminology).""" + self.objects.append(object) + + def escapexml(self, s) : + """Escape some xml entities.""" + s = string.strip(s) + s = string.replace(s, "&", "&") + s = string.replace(s, "<", "<") + return string.replace(s, ">", ">") + +def rlzope(self) : + """A sample external method to show people how to use ReportLab from within Zope.""" + try: + # + # which file/object name to use ? + # append ?name=xxxxx to rlzope's url to + # choose another name + filename = self.REQUEST.get("name", "dummy.pdf") + if filename[-4:] != '.pdf' : + filename = filename + '.pdf' + + # tell the browser we send some PDF document + # with the requested filename + + # get the document's content itself as a string of text + content = str(MyPDFDoc(self, filename)) + + # we will return it to the browser, but before that we also want to + # save it into the ZODB into the current folder + try : + self.manage_addFile(id = filename, file = content, title = "A sample PDF document produced with ReportLab", precondition = '', content_type = "application/pdf") + except : + # it seems an object with this name already exists in the ZODB: + # it's more secure to not replace it, since we could possibly + # destroy an important PDF document of this name. + pass + self.REQUEST.RESPONSE.setHeader('Content-Type', 'application/pdf') + self.REQUEST.RESPONSE.setHeader('Content-Disposition', 'attachment; filename=%s' % filename) + except: + import traceback, sys, cgi + content = sys.stdout = sys.stderr = cStringIO.StringIO() + self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/html') + traceback.print_exc() + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + content = '
%s
' % cgi.escape(content.getvalue()) + + # then we also return the PDF content to the browser + return content diff --git a/bin/reportlab/demos/stdfonts/00readme.txt b/bin/reportlab/demos/stdfonts/00readme.txt new file mode 100644 index 00000000000..9803d8e0c9d --- /dev/null +++ b/bin/reportlab/demos/stdfonts/00readme.txt @@ -0,0 +1,7 @@ +This lists out the standard 14 fonts +in a very plain and simple fashion. + +Notably, the output is huge - it makes +two separate text objects for each glyph. +Smarter programming would make tighter +PDF, but more lines of Python! diff --git a/bin/reportlab/demos/stdfonts/stdfonts.py b/bin/reportlab/demos/stdfonts/stdfonts.py new file mode 100644 index 00000000000..44ce94f4739 --- /dev/null +++ b/bin/reportlab/demos/stdfonts/stdfonts.py @@ -0,0 +1,74 @@ +#Copyright ReportLab Europe Ltd. 2000-2004 +#see license.txt for license details +#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/stdfonts/stdfonts.py +__version__=''' $Id: stdfonts.py 2830 2006-04-05 15:18:32Z rgbecker $ ''' +__doc__=""" +This generates tables showing the 14 standard fonts in both +WinAnsi and MacRoman encodings, and their character codes. +Supply an argument of 'hex' or 'oct' to get code charts +in those encodings; octal is what you need for \\n escape +sequences in Python literals. + +usage: standardfonts.py [dec|hex|oct] +""" +import sys +from reportlab.pdfbase import pdfmetrics +from reportlab.pdfgen import canvas +import string + +label_formats = {'dec':('%d=', 'Decimal'), + 'oct':('%o=','Octal'), + 'hex':('0x%x=', 'Hexadecimal')} + +def run(mode): + + label_formatter, caption = label_formats[mode] + + for enc in ['MacRoman', 'WinAnsi']: + canv = canvas.Canvas( + 'StandardFonts_%s.pdf' % enc, + ) + canv.setPageCompression(0) + + for faceName in pdfmetrics.standardFonts: + if faceName in ['Symbol', 'ZapfDingbats']: + encLabel = faceName+'Encoding' + else: + encLabel = enc + 'Encoding' + + fontName = faceName + '-' + encLabel + pdfmetrics.registerFont(pdfmetrics.Font(fontName, + faceName, + encLabel) + ) + + canv.setFont('Times-Bold', 18) + canv.drawString(80, 744, fontName) + canv.setFont('Times-BoldItalic', 12) + canv.drawRightString(515, 744, 'Labels in ' + caption) + + + #for dingbats, we need to use another font for the numbers. + #do two parallel text objects. + for byt in range(32, 256): + col, row = divmod(byt - 32, 32) + x = 72 + (66*col) + y = 720 - (18*row) + canv.setFont('Helvetica', 14) + canv.drawString(x, y, label_formatter % byt) + canv.setFont(fontName, 14) + canv.drawString(x+44, y, chr(byt).decode(encLabel,'ignore').encode('utf8')) + canv.showPage() + canv.save() + +if __name__ == '__main__': + if len(sys.argv)==2: + mode = string.lower(sys.argv[1]) + if mode not in ['dec','oct','hex']: + print __doc__ + + elif len(sys.argv) == 1: + mode = 'dec' + run(mode) + else: + print __doc__ diff --git a/bin/reportlab/demos/tests/testdemos.py b/bin/reportlab/demos/tests/testdemos.py new file mode 100644 index 00000000000..3a215bb2269 --- /dev/null +++ b/bin/reportlab/demos/tests/testdemos.py @@ -0,0 +1,14 @@ +#!/bin/env python +#Copyright ReportLab Europe Ltd. 2000-2004 +#see license.txt for license details +#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/tests/testdemos.py +__version__=''' $Id: testdemos.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' +__doc__='Test all demos' +_globals=globals().copy() +import os, sys +from reportlab import pdfgen + +for p in ('pythonpoint/pythonpoint.py','stdfonts/stdfonts.py','odyssey/odyssey.py', 'gadflypaper/gfe.py'): + fn = os.path.normcase(os.path.normpath(os.path.join(os.path.dirname(pdfgen.__file__),'..','demos',p))) + os.chdir(os.path.dirname(fn)) + execfile(fn,_globals.copy()) \ No newline at end of file