//======================================================================== // // Annot.cc // // Copyright 2000-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "goo/gmem.h" #include "Object.h" #include "Catalog.h" #include "Gfx.h" #include "Lexer.h" #include "UGooString.h" #include "Annot.h" //------------------------------------------------------------------------ // Annot //------------------------------------------------------------------------ Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict) { Object apObj, asObj, obj1, obj2; GBool regen, isTextField; double t; ok = gFalse; xref = xrefA; appearBuf = NULL; if (dict->lookup("Rect", &obj1)->isArray() && obj1.arrayGetLength() == 4) { //~ should check object types here obj1.arrayGet(0, &obj2); xMin = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); yMin = obj2.getNum(); obj2.free(); obj1.arrayGet(2, &obj2); xMax = obj2.getNum(); obj2.free(); obj1.arrayGet(3, &obj2); yMax = obj2.getNum(); obj2.free(); if (xMin > xMax) { t = xMin; xMin = xMax; xMax = t; } if (yMin > yMax) { t = yMin; yMin = yMax; yMax = t; } } else { //~ this should return an error xMin = yMin = 0; xMax = yMax = 1; } obj1.free(); // check if field apperances need to be regenerated regen = gFalse; if (acroForm) { acroForm->lookup("NeedAppearances", &obj1); if (obj1.isBool() && obj1.getBool()) { regen = gTrue; } obj1.free(); } // check for a text-type field isTextField = dict->lookup("FT", &obj1)->isName("Tx"); obj1.free(); #if 0 //~ appearance stream generation is not finished yet if (regen && isTextField) { generateAppearance(acroForm, dict); } else { #endif if (dict->lookup("AP", &apObj)->isDict()) { if (dict->lookup("AS", &asObj)->isName()) { if (apObj.dictLookup("N", &obj1)->isDict()) { if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) { obj2.copy(&appearance); ok = gTrue; } else { obj2.free(); if (obj1.dictLookupNF("Off", &obj2)->isRef()) { obj2.copy(&appearance); ok = gTrue; } } obj2.free(); } obj1.free(); } else { if (apObj.dictLookupNF("N", &obj1)->isRef()) { obj1.copy(&appearance); ok = gTrue; } obj1.free(); } asObj.free(); } apObj.free(); #if 0 //~ appearance stream generation is not finished yet } #endif } Annot::~Annot() { appearance.free(); if (appearBuf) { delete appearBuf; } } void Annot::generateAppearance(Dict *acroForm, Dict *dict) { MemStream *appearStream; Object daObj, vObj, drObj, appearDict, obj1, obj2; GooString *daStr, *daStr1, *vStr, *s; char buf[256]; double fontSize; int c; int i0, i1; //~ DA can be inherited if (dict->lookup("DA", &daObj)->isString()) { daStr = daObj.getString(); // look for a font size //~ may want to parse the DS entry in place of this (if it exists) daStr1 = NULL; fontSize = 10; for (i1 = daStr->getLength() - 2; i1 >= 0; --i1) { if (daStr->getChar(i1) == 'T' && daStr->getChar(i1+1) == 'f') { for (--i1; i1 >= 0 && Lexer::isSpace(daStr->getChar(i1)); --i1) ; for (i0 = i1; i0 >= 0 && !Lexer::isSpace(daStr->getChar(i0)); --i0) ; if (i0 >= 0) { ++i0; ++i1; s = new GooString(daStr, i0, i1 - i0); fontSize = atof(s->getCString()); delete s; // autosize the font if (fontSize == 0) { fontSize = 0.67 * (yMax - yMin); daStr1 = new GooString(daStr, 0, i0); sprintf(buf, "%.2f", fontSize); daStr1->append(buf); daStr1->append(daStr->getCString() + i1, daStr->getLength() - i1); } } break; } } // build the appearance stream contents appearBuf = new GooString(); appearBuf->append("/Tx BMC\n"); appearBuf->append("q BT\n"); appearBuf->append(daStr1 ? daStr1 : daStr)->append("\n"); if (dict->lookup("V", &vObj)->isString()) { //~ handle quadding -- this requires finding the font and using //~ the encoding and char widths sprintf(buf, "1 0 0 1 %.2f %.2f Tm\n", 2.0, yMax - yMin - fontSize); appearBuf->append(buf); sprintf(buf, "%g TL\n", fontSize); appearBuf->append(buf); vStr = vObj.getString(); i0 = 0; while (i0 < vStr->getLength()) { for (i1 = i0; i1 < vStr->getLength() && vStr->getChar(i1) != '\n' && vStr->getChar(i1) != '\r'; ++i1) ; if (i0 > 0) { appearBuf->append("T*\n"); } appearBuf->append('('); for (; i0 < i1; ++i0) { c = vStr->getChar(i0); if (c == '(' || c == ')' || c == '\\') { appearBuf->append('\\'); appearBuf->append(c); } else if (c < 0x20 || c >= 0x80) { sprintf(buf, "\\%03o", c); appearBuf->append(buf); } else { appearBuf->append(c); } } appearBuf->append(") Tj\n"); if (i1 + 1 < vStr->getLength() && vStr->getChar(i1) == '\r' && vStr->getChar(i1 + 1) == '\n') { i0 = i1 + 2; } else { i0 = i1 + 1; } } } vObj.free(); appearBuf->append("ET Q\n"); appearBuf->append("EMC\n"); // build the appearance stream dictionary appearDict.initDict(xref); appearDict.dictAdd("Length", obj1.initInt(appearBuf->getLength())); appearDict.dictAdd("Subtype", obj1.initName("Form")); obj1.initArray(xref); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(xMax - xMin)); obj1.arrayAdd(obj2.initReal(yMax - yMin)); appearDict.dictAdd("BBox", &obj1); // find the resource dictionary dict->lookup("DR", &drObj); if (!drObj.isDict()) { dict->lookup("Parent", &obj1); while (obj1.isDict()) { drObj.free(); obj1.dictLookup("DR", &drObj); if (drObj.isDict()) { break; } obj1.dictLookup("Parent", &obj2); obj1.free(); obj1 = obj2; } obj1.free(); if (!drObj.isDict()) { if (acroForm) { drObj.free(); acroForm->lookup("DR", &drObj); } } } if (drObj.isDict()) { appearDict.dictAdd("Resources", drObj.copy(&obj1)); } drObj.free(); // build the appearance stream appearStream = new MemStream(appearBuf->getCString(), 0, appearBuf->getLength(), &appearDict); appearance.initStream(appearStream); ok = gTrue; if (daStr1) { delete daStr1; } } daObj.free(); } void Annot::draw(Gfx *gfx) { Object obj; if (appearance.fetch(xref, &obj)->isStream()) { gfx->doAnnot(&obj, xMin, yMin, xMax, yMax); } obj.free(); } //------------------------------------------------------------------------ // Annots //------------------------------------------------------------------------ Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) { Dict *acroForm; Annot *annot; Object obj1; int size; int i; annots = NULL; size = 0; nAnnots = 0; acroForm = catalog->getAcroForm()->isDict() ? catalog->getAcroForm()->getDict() : NULL; if (annotsObj->isArray()) { for (i = 0; i < annotsObj->arrayGetLength(); ++i) { if (annotsObj->arrayGet(i, &obj1)->isDict()) { annot = new Annot(xref, acroForm, obj1.getDict()); if (annot->isOk()) { if (nAnnots >= size) { size += 16; annots = (Annot **)greallocn(annots, size, sizeof(Annot *)); } annots[nAnnots++] = annot; } else { delete annot; } } obj1.free(); } } } Annots::~Annots() { int i; for (i = 0; i < nAnnots; ++i) { delete annots[i]; } gfree(annots); }