]> git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/Annot.cxx
Load cups into easysw/current.
[thirdparty/cups.git] / pdftops / Annot.cxx
1 //========================================================================
2 //
3 // Annot.cc
4 //
5 // Copyright 2000-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <config.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 fprintf(stderr, "DEBUG: Annot::draw(gfx=%p)\n", gfx);
267
268 if (appearance.fetch(xref, &obj)->isStream()) {
269 gfx->doAnnot(&obj, xMin, yMin, xMax, yMax);
270 }
271 obj.free();
272 }
273
274 //------------------------------------------------------------------------
275 // Annots
276 //------------------------------------------------------------------------
277
278 Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
279 Dict *acroForm;
280 Annot *annot;
281 Object obj1;
282 int size;
283 int i;
284
285 annots = NULL;
286 size = 0;
287 nAnnots = 0;
288
289 acroForm = catalog->getAcroForm()->isDict() ?
290 catalog->getAcroForm()->getDict() : NULL;
291 if (annotsObj->isArray()) {
292 for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
293 if (annotsObj->arrayGet(i, &obj1)->isDict()) {
294 annot = new Annot(xref, acroForm, obj1.getDict());
295 if (annot->isOk()) {
296 if (nAnnots >= size) {
297 size += 16;
298 annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
299 }
300 annots[nAnnots++] = annot;
301 } else {
302 delete annot;
303 }
304 }
305 obj1.free();
306 }
307 }
308 }
309
310 Annots::~Annots() {
311 int i;
312
313 for (i = 0; i < nAnnots; ++i) {
314 delete annots[i];
315 }
316 gfree(annots);
317 }