1#aigen.py
2"""
3This is a library to generate AI files containing text and graphics.  It is
4part of the Adobe Illustrator format backend for PIDDLE foundation for a
5complete Reporting solution in Python.
6
7(C) Copyright Bill Bedford 1999
8parts (C) Copyright Andy Robinson 1998-1999
9"""
10
11import os
12import sys
13import string
14import time
15import tempfile
16from types import *
17
18
19##############################################################
20#
21#			Constants and declarations
22#
23##############################################################
24
25
26
27StandardEnglishFonts = [
28	'Courier', 'Courier-Bold', 'Courier-Oblique', 'Courier-BoldOblique',
29	'Helvetica', 'Helvetica-Bold', 'Helvetica-Oblique',
30	'Helvetica-BoldOblique',
31	'Times-Roman', 'Times-Bold', 'Times-Italic', 'Times-BoldItalic',
32	'Symbol','ZapfDingbats']
33
34AIError = 'AIError'
35AFMDIR = '.'
36
37##############################################################
38#
39#			AI Metrics
40# This is a preamble to give us a stringWidth function.
41# loads and caches AFM files, but won't need to as the
42# standard fonts are there already
43##############################################################
44
45widths = {'courier': [
460,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
470,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
48600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
49600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
50600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
51600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
52600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
53600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,0,
540,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
550,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
560,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
570,600,600,600,600,0,600,600,600,600,600,600,600,600,0,600,
580,600,600,600,600,600,600,600,600,0,600,600,0,600,600,600,
59600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
600,600,0,600,0,0,0,0,600,600,600,600,0,0,0,0,
610,600,0,0,0,600,0,0,600,600,600,600,0,0,600],
62'courier-bold': [
630,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
640,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
65600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
66600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
67600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
68600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
69600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
70600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,0,
710,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
720,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
730,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
740,600,600,600,600,0,600,600,600,600,600,600,600,600,0,600,
750,600,600,600,600,600,600,600,600,0,600,600,0,600,600,600,
76600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
770,600,0,600,0,0,0,0,600,600,600,600,0,0,0,0,
780,600,0,0,0,600,0,0,600,600,600,600,0,0,600],
79'courier-boldoblique': [
800,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
810,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
82600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
83600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
84600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
85600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
86600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
87600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,0,
880,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
890,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
900,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
910,600,600,600,600,0,600,600,600,600,600,600,600,600,0,600,
920,600,600,600,600,600,600,600,600,0,600,600,0,600,600,600,
93600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
940,600,0,600,0,0,0,0,600,600,600,600,0,0,0,0,
950,600,0,0,0,600,0,0,600,600,600,600,0,0,600],
96'courier-oblique': [
970,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
980,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
99600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
100600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
101600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
102600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
103600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
104600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,0,
1050,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1060,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1070,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,
1080,600,600,600,600,0,600,600,600,600,600,600,600,600,0,600,
1090,600,600,600,600,600,600,600,600,0,600,600,0,600,600,600,
110600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1110,600,0,600,0,0,0,0,600,600,600,600,0,0,0,0,
1120,600,0,0,0,600,0,0,600,600,600,600,0,0,600],
113'helvetica': [
1140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
116278,278,355,556,556,889,667,222,333,333,389,584,278,333,278,278,
117556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,
1181015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,
119667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,
120222,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,
121556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,0,
1220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1230,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1240,333,556,556,167,556,556,556,556,191,333,556,333,333,500,500,
1250,556,556,556,278,0,537,350,222,333,333,556,1000,1000,0,611,
1260,333,333,333,333,333,333,333,333,0,333,333,0,333,333,333,
1271000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1280,1000,0,370,0,0,0,0,556,778,1000,365,0,0,0,0,
1290,889,0,0,0,278,0,0,222,611,944,611,0,0,834],
130'helvetica-bold': [
1310,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1320,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
133278,333,474,556,556,889,722,278,333,333,389,584,278,333,278,278,
134556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,
135975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,
136667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,
137278,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,
138611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,0,
1390,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1400,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1410,333,556,556,167,556,556,556,556,238,500,556,333,333,611,611,
1420,556,556,556,278,0,556,350,278,500,500,556,1000,1000,0,611,
1430,333,333,333,333,333,333,333,333,0,333,333,0,333,333,333,
1441000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1450,1000,0,370,0,0,0,0,611,778,1000,365,0,0,0,0,
1460,889,0,0,0,278,0,0,278,611,944,611,0,0,834],
147'helvetica-boldoblique': [
1480,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1490,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
150278,333,474,556,556,889,722,278,333,333,389,584,278,333,278,278,
151556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,
152975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,
153667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,
154278,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,
155611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,0,
1560,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1570,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1580,333,556,556,167,556,556,556,556,238,500,556,333,333,611,611,
1590,556,556,556,278,0,556,350,278,500,500,556,1000,1000,0,611,
1600,333,333,333,333,333,333,333,333,0,333,333,0,333,333,333,
1611000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1620,1000,0,370,0,0,0,0,611,778,1000,365,0,0,0,0,
1630,889,0,0,0,278,0,0,278,611,944,611,0,0,834],
164'helvetica-oblique': [
1650,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1660,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
167278,278,355,556,556,889,667,222,333,333,389,584,278,333,278,278,
168556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,
1691015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,
170667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,
171222,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,
172556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,0,
1730,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1740,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1750,333,556,556,167,556,556,556,556,191,333,556,333,333,500,500,
1760,556,556,556,278,0,537,350,222,333,333,556,1000,1000,0,611,
1770,333,333,333,333,333,333,333,333,0,333,333,0,333,333,333,
1781000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1790,1000,0,370,0,0,0,0,556,778,1000,365,0,0,0,0,
1800,889,0,0,0,278,0,0,222,611,944,611,0,0,834],
181'symbol': [
1820,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1830,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
184250,333,713,500,549,833,778,439,333,333,500,549,250,549,250,278,
185500,500,500,500,500,500,500,500,500,500,278,278,549,549,549,444,
186549,722,667,722,612,611,763,603,722,333,631,722,686,889,722,722,
187768,741,556,592,611,690,439,768,645,795,611,333,863,333,658,500,
188500,631,549,549,494,439,521,411,603,329,603,549,549,576,521,549,
189549,521,549,603,439,576,713,686,493,686,494,480,200,480,549,0,
1900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1910,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1920,620,247,549,167,713,500,753,753,753,753,1042,987,603,987,603,
193400,549,411,549,549,713,494,460,549,549,549,549,1000,603,1000,658,
194823,686,795,987,768,768,823,768,768,713,713,713,713,713,713,713,
195768,713,790,790,890,823,549,250,713,603,603,1042,987,603,987,603,
196494,329,790,790,786,713,384,384,384,384,384,384,494,494,494,494,
1970,329,274,686,686,686,384,384,384,384,384,384,494,494,790],
198'times-bold': [
1990,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
201250,333,555,500,500,1000,833,333,333,333,500,570,250,333,250,278,
202500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,
203930,722,667,722,722,667,611,778,778,389,500,778,667,944,722,778,
204611,778,722,556,667,722,722,1000,722,722,667,333,278,333,581,500,
205333,500,556,444,556,444,333,500,556,278,333,556,278,833,556,500,
206556,556,444,389,333,556,500,722,500,500,444,394,220,394,520,0,
2070,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2080,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2090,333,500,500,167,500,500,500,500,278,500,500,333,333,556,556,
2100,500,500,500,250,0,540,350,333,500,500,500,1000,1000,0,500,
2110,333,333,333,333,333,333,333,333,0,333,333,0,333,333,333,
2121000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2130,1000,0,300,0,0,0,0,667,778,1000,330,0,0,0,0,
2140,722,0,0,0,278,0,0,278,500,722,556,0,0,750],
215'times-bolditalic': [
2160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
218250,389,555,500,500,833,778,333,333,333,500,570,250,333,250,278,
219500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,
220832,667,667,667,722,667,667,722,778,389,500,667,611,889,722,722,
221611,722,667,556,611,722,667,889,667,611,611,333,278,333,570,500,
222333,500,500,444,500,444,333,500,556,278,278,500,278,778,556,500,
223500,500,389,389,278,556,444,667,500,444,389,348,220,348,570,0,
2240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2250,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2260,389,500,500,167,500,500,500,500,278,500,500,333,333,556,556,
2270,500,500,500,250,0,500,350,333,500,500,500,1000,1000,0,500,
2280,333,333,333,333,333,333,333,333,0,333,333,0,333,333,333,
2291000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2300,944,0,266,0,0,0,0,611,722,944,300,0,0,0,0,
2310,722,0,0,0,278,0,0,278,500,722,500,0,0,750],
232'times-italic': [
2330,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2340,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
235250,333,420,500,500,833,778,333,333,333,500,675,250,333,250,278,
236500,500,500,500,500,500,500,500,500,500,333,333,675,675,675,500,
237920,611,611,667,722,611,611,722,722,333,444,667,556,833,667,722,
238611,722,611,500,556,722,611,833,611,556,556,389,278,389,422,500,
239333,500,500,444,500,444,278,500,500,278,278,444,278,722,500,500,
240500,500,389,389,278,500,444,667,444,444,389,400,275,400,541,0,
2410,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2420,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2430,389,500,500,167,500,500,500,500,214,556,500,333,333,500,500,
2440,500,500,500,250,0,523,350,333,556,556,500,889,1000,0,500,
2450,333,333,333,333,333,333,333,333,0,333,333,0,333,333,333,
246889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2470,889,0,276,0,0,0,0,556,722,944,310,0,0,0,0,
2480,667,0,0,0,278,0,0,278,500,667,500,0,0,750],
249'times-roman': [
2500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2510,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
252250,333,408,500,500,833,778,333,333,333,500,564,250,333,250,278,
253500,500,500,500,500,500,500,500,500,500,278,278,564,564,564,444,
254921,722,667,667,722,611,556,722,722,333,389,722,611,889,722,722,
255556,722,667,556,611,722,722,944,722,722,611,333,278,333,469,500,
256333,444,500,444,500,444,333,500,500,278,278,500,278,778,500,500,
257500,500,333,389,278,500,500,722,500,500,444,480,200,480,541,0,
2580,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2590,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2600,333,500,500,167,500,500,500,500,180,444,500,333,333,556,556,
2610,500,500,500,250,0,453,350,333,444,444,500,1000,1000,0,444,
2620,333,333,333,333,333,333,333,333,0,333,333,0,333,333,333,
2631000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2640,889,0,276,0,0,0,0,611,722,889,310,0,0,0,0,
2650,667,0,0,0,278,0,0,278,500,722,500,0,0,750],
266'zapfdingbats': [
2670,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2680,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
269278,974,961,974,980,719,789,790,791,690,960,939,549,855,911,933,
270911,945,974,755,846,762,761,571,677,763,760,759,754,494,552,537,
271577,692,786,788,788,790,793,794,816,823,789,841,823,833,816,831,
272923,744,723,749,790,792,695,776,768,792,759,707,708,682,701,826,
273815,789,789,707,687,696,689,786,787,713,791,785,791,873,761,762,
274762,759,759,892,892,788,784,438,138,277,415,392,392,668,668,0,
2750,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2760,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2770,732,544,544,910,667,760,760,776,595,694,626,788,788,788,788,
278788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,
279788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,
280788,788,788,788,894,838,1016,458,748,924,748,918,927,928,928,834,
281873,828,924,924,917,930,931,463,883,836,836,867,867,696,696,874,
2820,874,760,946,771,865,771,888,967,888,831,873,927,970,234]
283}
284
285ascent_descent = {'Courier': (629, -157),
286 'Courier-Bold': (626, -142),
287 'Courier-BoldOblique': (626, -142),
288 'Courier-Oblique': (629, -157),
289 'Helvetica': (718, -207),
290 'Helvetica-Bold': (718, -207),
291 'Helvetica-BoldOblique': (718, -207),
292 'Helvetica-Oblique': (718, -207),
293 'Symbol': (0, 0),
294 'Times-Bold': (676, -205),
295 'Times-BoldItalic': (699, -205),
296 'Times-Italic': (683, -205),
297 'Times-Roman': (683, -217),
298 'ZapfDingbats': (0, 0)}
299
300
301def parseAFMfile(filename):
302	"""Returns an array holding the widths of all characters in the font.
303	Ultra-crude parser"""
304	alllines = open(filename, 'r').readlines()
305	# get stuff between StartCharMetrics and EndCharMetrics
306	metriclines = []
307	between = 0
308	for line in alllines:
309		if string.find(string.lower(line), 'endcharmetrics') > -1:
310			between = 0
311			break
312		if between:
313			metriclines.append(line)
314		if string.find(string.lower(line), 'startcharmetrics') > -1:
315			between = 1
316
317	# break up - very shaky assumption about array size
318	widths = [0] * 255
319
320	for line in metriclines:
321		chunks = string.split(line, ';')
322
323		(c, cid) = string.split(chunks[0])
324		(wx, width) = string.split(chunks[1])
325		#(n, name) = string.split(chunks[2])
326		#(b, x1, y1, x2, y2) = string.split(chunks[3])
327		widths[string.atoi(cid)] = string.atoi(width)
328
329	# by default, any empties should get the width of a space
330	for i in range(len(widths)):
331		if widths[i] == 0:
332			widths[i] == widths[32]
333
334	return widths
335
336
337class FontCache:
338	"""Loads and caches font width information on demand.  Font names
339	converted to lower case for indexing.  Public interface is stringwidth"""
340	def __init__(self):
341		global widths
342		self.__widtharrays = widths
343
344
345	def loadfont(self, fontname):
346		filename = AFMDIR + os.sep + fontname + '.afm'
347		print 'cache loading',filename
348		assert os.path.exists(filename)
349		widths = parseAFMfile(filename)
350		self.__widtharrays[fontname] = widths
351	def getfont(self, fontname):
352		try:
353			return self.__widtharrays[fontname]
354		except:
355			try:
356				self.loadfont(fontname)
357				return self.__widtharrays[fontname]
358			except:
359				# font not found, use Courier
360				print 'Font',fontname,'not found - using Courier for widths'
361				return self.getfont('courier')
362
363
364	def stringwidth(self, text, font):
365		widths = self.getfont(string.lower(font))
366		w = 0
367		for char in text:
368			w = w + widths[ord(char)]
369		return w
370	def status(self):
371		#returns loaded fonts
372		return self.__widtharrays.keys()
373
374TheFontCache = FontCache()
375
376stringwidth = TheFontCache.stringwidth
377
378
379##############################################################
380#
381#			AI Document
382#
383##############################################################
384
385
386class AIDocument:
387	def __init__(self):
388		self.objects = []
389		self.info = AIHeader()  #hang onto it!
390		self.add(self.info)
391		self.transforms = AIStream()
392
393		self.pages = []
394		self.pagepositions = []
395		self.infopos = 19
396
397		# position 1
398#		cat = AICatalog()
399#		cat.RefPages = 3
400#		cat.RefOutlines = 2
401#		self.add(cat)
402
403		# position 2 - outlines
404#		outl = AIOutline()
405#		self.add(outl)
406
407		# position 3 - pages collection
408#		self.PageCol = AIPageCollection()
409#		self.add(self.PageCol)
410
411		# positions 4-17 - fonts
412#		fonts = MakeType1Fonts()
413#		for font in fonts:
414#			self.add(font)
415#
416#		self.fontdict = MakeFontDictionary(4, 15)
417
418		# position 18 - Info
419
420
421				# testing here - position 19 - an image
422		self.add(AIProlog())
423		self.add(AISetUp())
424#		self.add(AIStream())
425#		self.add(AIImage())
426		self.add(self.transforms)
427
428	def add(self, obj):
429		self.objects.append(obj)
430		obj.doc = self
431
432	def setTitle(self, title):
433		"embeds in AI file"
434		self.info.title = title
435
436	def setAuthor(self, author):
437		"embedded in AI file"
438		self.info.author = author
439
440	def setBoundingBox(self, boundingbox):
441		"embeds in AI file"
442		self.transforms.originx = 0
443		self.transforms.originy = 0
444#		self.info.boundingBox = boundingbox
445		lx,ly, ux,uy, tx = boundingbox
446		print 'setBoundingBox', lx,ly, ux,uy, tx
447		print 'setBoundingBox', ux-lx,uy-ly
448		self.info.pagesize = (ux-lx), (uy-ly)
449		##XXX If the ArtSize is smaller than Letter Freehand always draws the
450		##XXX origin as if the Art board was Letter sized, however the arboard
451		##XXX is drawn at the same center as a letter sized artboard.
452		##XXX hence this translation
453		if ux-lx < 612:
454			self.transforms.originx = w = (612 - (ux-lx))/2 +tx
455			lx, ux = lx + w, ux + w
456		if uy-ly < 792:
457			self.transforms.originy = w = (792 - (uy-ly))/2 +tx
458			ly, uy = ly + w, uy + w
459#		print self.transforms
460#		print self.transforms.originx
461#		print self.transforms.originy
462		print 'setBoundingBox', lx,ly, ux,uy
463		self.info.boundingBox = lx, ly, ux, uy
464		self.transforms.height = uy
465
466	def setPage(self, page):
467#		print 'setPage', page
468		self.transforms.data = page
469
470
471	def SaveToFile(self, filename):
472#		print 'SaveToFile', self.transforms.originx, self.transforms.originy
473		f = open(filename, 'w')
474		old_output = sys.stdout
475		sys.stdout = f
476		self.printAI()
477		sys.stdout = old_output
478		f.close()
479
480	def printXref(self):
481		self.startxref = sys.stdout.tell()
482		print 'xref'
483		print 0,len(self.objects) + 1
484		print '0000000000 65535 f'
485		for pos in self.xref:
486			print '%0.10d 00000 n' % pos
487
488	def printTrailer(self):
489		print '''%%PageTrailer
490gsave annotatepage grestore showpage
491%%Trailer'''
492#		print '<< /Size %d /Root %d 0 R /Info %d 0 R>>' % (len(self.objects) + 1, 1, self.infopos)
493#		print 'startxref'
494#		print self.startxref
495
496	def printAI(self):
497		"prints it to standard output.  Logs positions for doing trailer"
498#		print "%AI-1.0"
499#		print "%����"
500		i = 1
501		self.xref = []
502#		print self.objects
503		for obj in self.objects:
504#			print 'printAI', obj
505#			pos = sys.stdout.tell()
506#			self.xref.append(pos)
507#			print i, '0 obj'
508			obj.printAI()
509#			print 'endobj'
510#			i = i + 1
511#		self.printXref()
512		self.printTrailer()
513		print "%%EOF",
514
515
516	def addPage(self, page):
517		"""adds page and stream at end.  Maintains pages list"""
518		#page.buildstream()
519		pos = len(self.objects) # work out where added
520
521#		page.ParentPos = 3   #pages collection
522#		page.info = {'parentpos':3,
523#			'fontdict':self.fontdict,
524#			'contentspos':pos + 2}
525
526#		self.PageCol.PageList.append(pos+1)
527#		print 'addPage', self.transforms.setStream((10, 20, 'm'))
528#		print 'addPage', self.transforms.setStream(page)
529#		self.page =
530		self.transforms.data = page
531#		print 'addPage', self.page
532#		self.objects.append(page)
533#		self.objects.append(self.page)
534
535
536
537##############################################################
538#
539#			Utilities
540#
541##############################################################
542
543class OutputGrabber:
544	"""At times we need to put something in the place of standard
545	output.  This grabs stdout, keeps the data, and releases stdout
546	when done.
547
548	NOT working well enough!"""
549	def __init__(self):
550		self.oldoutput = sys.stdout
551		sys.stdout = self
552		self.closed = 0
553		self.data = []
554	def write(self, x):
555		if not self.closed:
556			self.data.append(x)
557
558	def getData(self):
559		return string.join(self.data)
560
561	def close(self):
562		sys.stdout = self.oldoutput
563		self.closed = 1
564
565	def __del__(self):
566		if not self.closed:
567			self.close()
568
569
570def testOutputGrabber():
571	gr = OutputGrabber()
572	for i in range(10):
573		print 'line',i
574	data = gr.getData()
575	gr.close()
576	print 'Data...',data
577
578
579##############################################################
580#
581#			AI Object Hierarchy
582#
583##############################################################
584
585
586
587class AIObject:
588	"Base class for all AI objects"
589	def printAI(self):
590		print '% base AI object'
591
592
593class AILiteral(AIObject):
594	" a ready-made one you wish to quote"
595	def __init__(self, text):
596		self.text = text
597	def printAI(self):
598		print self.text
599
600
601
602class AICatalog(AIObject):
603	"requires RefPages and RefOutlines set"
604	def __init__(self):
605		self.template = '''<<
606/Type /Catalog
607/Pages %d 0 R
608/Outlines %d 0 R
609>>'''
610	def printAI(self):
611		print self.template % (self.RefPages, self.RefOutlines)
612
613class AIHeader(AIObject):
614	# no features implemented yet
615	def __init__(self):
616		self.title = "untitled"
617		self.author = "anonymous"
618		self.boundingBox = (0, 0, 565, 842)
619		self.pagesize = (565, 842)
620		self.rulerUnits	= 2
621		now = time.localtime(time.time())
622		self.datestr = time.strftime("%x %I:%M %p", now)
623
624	def printAI(self):
625		print "%!PS-Adobe-3.0"
626		print "%%Creator: PIDDLE Adobe Illustrator backend"
627		print "%%Title: " +'(%s)' % self.title
628		print "%%For: " +'(%s)' % self.author
629		print "%%CreationDate: " +'(%s)' % self.datestr
630		print "%%DocumentProcessColors: Black"""
631		print '%%BoundingBox: ' + '%s %s %s %s' % self.boundingBox
632		#%%DocumentProcessColors: Cyan Magenta Yellow
633		#%%DocumentCustomColors: (PANTONE 156 CV)
634		#%%RGBCustomColor: red green blue (customcolorname)
635		#%%DocumentFonts: CooperBlack
636		#%%+ Minion-Regular
637		#%%DocumentFiles: WrathOfRalph
638		print "%AI5_FileFormat 3"
639		print "%AI3_ColorUsage: Color"
640		print '%AI5_ArtSize: ' + '%s %s' % self.pagesize
641		print '%AI5_Templatebox: ' + '%s %s' % self.pagesize
642		#%AI7_ImageSettings: flag
643		print '%AI5_TargetResolution: 300'
644		print '%%EndComments'
645
646
647class AIProlog(AIObject):
648	"null outline, does nothing yet"
649	def __init__(self):
650		self.FontList = []
651	def printAI(self):
652		print '%%BeginProlog'
653		print '%%EndProlog'
654
655class AISetUp(AIObject):
656	"null outline, does nothing yet"
657	def __init__(self):
658		self.FontList = []
659	def printAI(self):
660		print '%%BeginSetup'
661		if self.FontList:
662			pass
663		print '%%EndSetup'
664
665class AIPageCollection(AIObject):
666	"presumes PageList attribute set (list of integers)"
667	def __init__(self):
668		self.PageList = []
669	def printAI(self):
670		result = '<<\n/Type /Pages\n/Count %d\n/Kids [' % len(self.PageList)
671		for page in self.PageList:
672			result = result + str(page) + ' 0 R '
673		result = result + ']\n>>'
674		print result
675
676#class AIBody(AIObject):
677#	"""The Bastard.  Needs list of Resources etc. Use a standard one for now.
678#	It manages a AIStream object which must be added to the document's list
679#	os objects as well."""
680#	def __init__(self):
681#		self.drawables = []
682#		self.stream = AIStream()
683#		self.hasImages = 0
684#		self.template = """<<
685#/Type /Page
686#/Parent %(parentpos)d 0 R
687#/Resources
688#	<<
689#	/Font %(fontdict)s
690#	/ProcSet %(procsettext)s
691#	>>
692#/MediaBox [0 0 595 842]
693#/Contents %(contentspos)d 0 R
694#>>"""
695#	def printAI(self):
696#		# check for image support
697##		if self.hasImages:
698##			self.info['procsettext'] = '[/AI /Text /ImageC]'
699##		else:
700##			self.info['procsettext'] = '[/AI /Text]'
701##
702##		print self.template % self.info
703#		print "AIBody.printAI(self)"
704#
705#	def clear(self):
706#		self.drawables = []
707#
708#	def setStream(self, data):
709#		self.stream.setStream(data)
710#
711#
712#	#def add(self, drawable):
713#	#	self.drawables.append(drawable)
714#	#
715#	#def buildstream(self):
716#	#	textmode = 0
717#	#
718#	#	oldout = sys.stdout
719#	#	fn = tempfile.mktemp()
720#	#	#f = open(fn, 'wb')  #AR 19980202
721#	#	f = open(fn, 'w')
722#	#	sys.stdout = f
723#	#	text = 0
724#	#	for obj in self.drawables:
725#	#		if obj.isText():
726#	#			if text == 0:
727#	#				print 'BT'
728#	#				text = 1
729#	#		else:
730#	#			if text == 1:
731#	#				print 'ET'
732#	#				text = 0
733#	#
734#	#		obj.printAI()
735#	#	if self.drawables[-1].isText():
736#	#		print 'ET'
737#	#	sys.stdout = oldout
738#	#	f.close()
739#	#
740#	#	self.stream = AIStream()
741#	#	#self.stream.data = open(fn,'r').read()
742#	#	self.stream.data = open(fn,'rb').read()	#AR 19980202
743#
744#
745#
746#
747TestStream = ['q', (72, 720, 'l'), 'S', 'Q', (80, 672, 'm'), (24, 'TL'),
748(('Test Page with no stream'), 'Tj', 'T*')]
749
750
751class AIStream(AIObject):
752	"Used for the contents of a page"
753	def __init__(self):
754		self.data = []
755		self.originx = 0
756		self.originy = 0
757		self.height = 0
758
759#	def setStream(self, data):
760#		self.data = data
761#		print 'setStream', self.originy, self.originy
762#		print self.data
763
764	def printAI(self):
765		# test code is useful
766		if self.data == None:
767			self.data = TestStream
768		# the AI length key should contain the length including
769		# any extra LF pairs added by Print on DOS.
770
771#		lines = len(string.split(self.data,'\n'))
772#		length = len(self.data) + lines   # one extra LF each
773		#length = len(self.data)	#AR 19980202
774#		print 'printAI', self.originx, self.originy
775#		print 'printAI', self.transformAI(self.originx, self.originy, self.height)
776
777
778#		print '<< /Length %d >>' % length
779		print '''%AI5_BeginLayer
7801 1 1 1 0 0 0 79 128 255 Lb
781(Foreground) Ln'''
782		print self.transformAI(self.originx, self.originy, self.height)
783
784#		print 'XXXX', self.data
785		print '''LB
786%AI5_EndLayer--'''
787
788	def transformAI(self, ox, oy, ty):
789#		print 'transformAI', ox, oy
790#		print 'transformAI', self.data, type(self.data)
791		page = []
792		for line in self.data:
793#			print line
794			if type(line) == TupleType and  len(line) == 3:
795#				print 'x', line
796				line =  line[0]+ ox,  line[1]+oy, line[2]
797				line =  line[0],  oy + ty -line[1], line[2]
798				line = '%f %f %s' % line
799			elif type(line) == TupleType and  len(line) == 7:
800				line =  line[0]+ox,  line[1]+oy, line[2]+ox,  line[3]+oy, line[4]+ox,  line[5]+oy, line[6]
801				line =  line[0],  oy+ty-line[1], line[2],  oy+ty-line[3], line[4],  oy+ty-line[5], line[6]
802				line = '%f %f %f %f %f %f %s' % line
803#			print line
804			page.append(line)
805		return string.join(page, '\n')
806
807class AIImage(AIObject):
808	def printAI(self):
809		print """<<
810/Type /XObject
811/Subtype /Image
812/Name /Im0
813/Width 24
814/Height 23
815/BitsPerComponent 1
816/ColorSpace /DeviceGray
817/Filter /ASCIIHexDecode
818/Length 174
819>>
820stream
821003B00 002700 002480 0E4940 114920 14B220 3CB650
82275FE88 17FF8C 175F14 1C07E2 3803C4 703182 F8EDFC
823B2BBC2 BB6F84 31BFC2 18EA3C 0E3E00 07FC00 03F800
8241E1800 1FF800>
825endstream
826endobj"""
827
828class AIType1Font(AIObject):
829	def __init__(self, key, font):
830		self.fontname = font
831		self.keyname = key
832		self.template = """<<
833/Type /Font
834/Subtype /Type1
835/Name /%s
836/BaseFont /%s
837/Encoding /WinAnsiEncoding
838>>"""
839	def printAI(self):
840		print self.template % (self.keyname, self.fontname)
841
842class AIProcSet(AIObject):
843	def printAI(self):
844		print "[/AI /Text]"
845
846
847
848
849
850
851
852##############################################################
853#
854#			some helpers
855#
856##############################################################
857
858def MakeType1Fonts():
859	"returns a list of all the standard font objects"
860	fonts = []
861	pos = 1
862	for fontname in StandardEnglishFonts:
863		font = AIType1Font('F'+str(pos), fontname)
864		fonts.append(font)
865		pos = pos + 1
866	return fonts
867
868def MakeFontDictionary(startpos, count):
869	"returns a font dictionary assuming they are all in the file from startpos"
870	dict = "		<< \n"
871	pos = startpos
872	for i in range(count-1):
873		dict = dict + '\t\t/F%d %d 0 R \n' % (i + 1, startpos + i)
874	dict = dict + "		>>\n"
875	return dict
876
877
878#if __name__ == '__main__':
879#	print 'For test scripts, run test1.py to test7.py'