added gfxmatrix_transform()
[swftools.git] / pdf2swf / xpdf / Annot.cc
1 //========================================================================
2 //
3 // Annot.cc
4 //
5 // Copyright 2000-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <stdlib.h>
16 #include "gmem.h"
17 #include "Object.h"
18 #include "Catalog.h"
19 #include "Gfx.h"
20 #include "Lexer.h"
21 #include "Annot.h"
22
23 //------------------------------------------------------------------------
24 // Annot
25 //------------------------------------------------------------------------
26
27 Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict) {
28   Object apObj, asObj, obj1, obj2;
29   GBool regen, isTextField;
30   double t;
31
32   ok = gFalse;
33   xref = xrefA;
34   appearBuf = NULL;
35
36   if (dict->lookup("Rect", &obj1)->isArray() &&
37       obj1.arrayGetLength() == 4) {
38     //~ should check object types here
39     obj1.arrayGet(0, &obj2);
40     xMin = obj2.getNum();
41     obj2.free();
42     obj1.arrayGet(1, &obj2);
43     yMin = obj2.getNum();
44     obj2.free();
45     obj1.arrayGet(2, &obj2);
46     xMax = obj2.getNum();
47     obj2.free();
48     obj1.arrayGet(3, &obj2);
49     yMax = obj2.getNum();
50     obj2.free();
51     if (xMin > xMax) {
52       t = xMin; xMin = xMax; xMax = t;
53     }
54     if (yMin > yMax) {
55       t = yMin; yMin = yMax; yMax = t;
56     }
57   } else {
58     //~ this should return an error
59     xMin = yMin = 0;
60     xMax = yMax = 1;
61   }
62   obj1.free();
63
64   // check if field apperances need to be regenerated
65   regen = gFalse;
66   if (acroForm) {
67     acroForm->lookup("NeedAppearances", &obj1);
68     if (obj1.isBool() && obj1.getBool()) {
69       regen = gTrue;
70     }
71     obj1.free();
72   }
73
74   // check for a text-type field
75   isTextField = dict->lookup("FT", &obj1)->isName("Tx");
76   obj1.free();
77
78 #if 0 //~ appearance stream generation is not finished yet
79   if (regen && isTextField) {
80     generateAppearance(acroForm, dict);
81   } else {
82 #endif
83     if (dict->lookup("AP", &apObj)->isDict()) {
84       if (dict->lookup("AS", &asObj)->isName()) {
85         if (apObj.dictLookup("N", &obj1)->isDict()) {
86           if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {
87             obj2.copy(&appearance);
88             ok = gTrue;
89           } else {
90             obj2.free();
91             if (obj1.dictLookupNF("Off", &obj2)->isRef()) {
92               obj2.copy(&appearance);
93               ok = gTrue;
94             }
95           }
96           obj2.free();
97         }
98         obj1.free();
99       } else {
100         if (apObj.dictLookupNF("N", &obj1)->isRef()) {
101           obj1.copy(&appearance);
102           ok = gTrue;
103         }
104         obj1.free();
105       }
106       asObj.free();
107     }
108     apObj.free();
109 #if 0 //~ appearance stream generation is not finished yet
110   }
111 #endif
112 }
113
114 Annot::~Annot() {
115   appearance.free();
116   if (appearBuf) {
117     delete appearBuf;
118   }
119 }
120
121 void Annot::generateAppearance(Dict *acroForm, Dict *dict) {
122   MemStream *appearStream;
123   Object daObj, vObj, drObj, appearDict, obj1, obj2;
124   GString *daStr, *daStr1, *vStr, *s;
125   char buf[256];
126   double fontSize;
127   int c;
128   int i0, i1;
129
130   //~ DA can be inherited
131   if (dict->lookup("DA", &daObj)->isString()) {
132     daStr = daObj.getString();
133
134     // look for a font size
135     //~ may want to parse the DS entry in place of this (if it exists)
136     daStr1 = NULL;
137     fontSize = 10;
138     for (i1 = daStr->getLength() - 2; i1 >= 0; --i1) {
139       if (daStr->getChar(i1) == 'T' && daStr->getChar(i1+1) == 'f') {
140         for (--i1; i1 >= 0 && Lexer::isSpace(daStr->getChar(i1)); --i1) ;
141         for (i0 = i1; i0 >= 0 && !Lexer::isSpace(daStr->getChar(i0)); --i0) ;
142         if (i0 >= 0) {
143           ++i0;
144           ++i1;
145           s = new GString(daStr, i0, i1 - i0);
146           fontSize = atof(s->getCString());
147           delete s;
148
149           // autosize the font
150           if (fontSize == 0) {
151             fontSize = 0.67 * (yMax - yMin);
152             daStr1 = new GString(daStr, 0, i0);
153             sprintf(buf, "%.2f", fontSize);
154             daStr1->append(buf);
155             daStr1->append(daStr->getCString() + i1,
156                            daStr->getLength() - i1);
157           }
158         }
159         break;
160       }
161     }
162
163     // build the appearance stream contents
164     appearBuf = new GString();
165     appearBuf->append("/Tx BMC\n");
166     appearBuf->append("q BT\n");
167     appearBuf->append(daStr1 ? daStr1 : daStr)->append("\n");
168     if (dict->lookup("V", &vObj)->isString()) {
169       //~ handle quadding -- this requires finding the font and using
170       //~   the encoding and char widths
171       sprintf(buf, "1 0 0 1 %.2f %.2f Tm\n", 2.0, yMax - yMin - fontSize);
172       appearBuf->append(buf);
173       sprintf(buf, "%g TL\n", fontSize);
174       appearBuf->append(buf);
175       vStr = vObj.getString();
176       i0 = 0;
177       while (i0 < vStr->getLength()) {
178         for (i1 = i0;
179              i1 < vStr->getLength() &&
180                vStr->getChar(i1) != '\n' && vStr->getChar(i1) != '\r';
181              ++i1) ;
182         if (i0 > 0) {
183           appearBuf->append("T*\n");
184         }
185         appearBuf->append('(');
186         for (; i0 < i1; ++i0) {
187           c = vStr->getChar(i0);
188           if (c == '(' || c == ')' || c == '\\') {
189             appearBuf->append('\\');
190             appearBuf->append(c);
191           } else if (c < 0x20 || c >= 0x80) {
192             sprintf(buf, "\\%03o", c);
193             appearBuf->append(buf);
194           } else {
195             appearBuf->append(c);
196           }
197         }
198         appearBuf->append(") Tj\n");
199         if (i1 + 1 < vStr->getLength() &&
200             vStr->getChar(i1) == '\r' && vStr->getChar(i1 + 1) == '\n') {
201           i0 = i1 + 2;
202         } else {
203           i0 = i1 + 1;
204         }
205       }
206     }
207     vObj.free();
208     appearBuf->append("ET Q\n");
209     appearBuf->append("EMC\n");
210
211     // build the appearance stream dictionary
212     appearDict.initDict(xref);
213     appearDict.dictAdd(copyString("Length"),
214                        obj1.initInt(appearBuf->getLength()));
215     appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
216     obj1.initArray(xref);
217     obj1.arrayAdd(obj2.initReal(0));
218     obj1.arrayAdd(obj2.initReal(0));
219     obj1.arrayAdd(obj2.initReal(xMax - xMin));
220     obj1.arrayAdd(obj2.initReal(yMax - yMin));
221     appearDict.dictAdd(copyString("BBox"), &obj1);
222
223     // find the resource dictionary
224     dict->lookup("DR", &drObj);
225     if (!drObj.isDict()) {
226       dict->lookup("Parent", &obj1);
227       while (obj1.isDict()) {
228         drObj.free();
229         obj1.dictLookup("DR", &drObj);
230         if (drObj.isDict()) {
231           break;
232         }
233         obj1.dictLookup("Parent", &obj2);
234         obj1.free();
235         obj1 = obj2;
236       }
237       obj1.free();
238       if (!drObj.isDict()) {
239         if (acroForm) {
240           drObj.free();
241           acroForm->lookup("DR", &drObj);
242         }
243       }
244     }
245     if (drObj.isDict()) {
246       appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
247     }
248     drObj.free();
249
250     // build the appearance stream
251     appearStream = new MemStream(appearBuf->getCString(), 0,
252                                  appearBuf->getLength(), &appearDict);
253     appearance.initStream(appearStream);
254     ok = gTrue;
255
256     if (daStr1) {
257       delete daStr1;
258     }
259   }
260   daObj.free();
261 }
262
263 void Annot::draw(Gfx *gfx) {
264   Object obj;
265
266   if (appearance.fetch(xref, &obj)->isStream()) {
267     gfx->doAnnot(&obj, xMin, yMin, xMax, yMax);
268   }
269   obj.free();
270 }
271
272 //------------------------------------------------------------------------
273 // Annots
274 //------------------------------------------------------------------------
275
276 Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
277   Dict *acroForm;
278   Annot *annot;
279   Object obj1;
280   int size;
281   int i;
282
283   annots = NULL;
284   size = 0;
285   nAnnots = 0;
286
287   acroForm = catalog->getAcroForm()->isDict() ?
288                catalog->getAcroForm()->getDict() : NULL;
289   if (annotsObj->isArray()) {
290     for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
291       if (annotsObj->arrayGet(i, &obj1)->isDict()) {
292         annot = new Annot(xref, acroForm, obj1.getDict());
293         if (annot->isOk()) {
294           if (nAnnots >= size) {
295             size += 16;
296             annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
297           }
298           annots[nAnnots++] = annot;
299         } else {
300           delete annot;
301         }
302       }
303       obj1.free();
304     }
305   }
306 }
307
308 Annots::~Annots() {
309   int i;
310
311   for (i = 0; i < nAnnots; ++i) {
312     delete annots[i];
313   }
314   gfree(annots);
315 }