diff -ur ./CHANGES ../../work/xpdf-3.02/CHANGES --- ./CHANGES 2007-02-27 23:05:51.000000000 +0100 +++ ../../work/xpdf-3.02/CHANGES 2008-07-15 16:57:35.000000000 +0200 @@ -1760,3 +1760,67 @@ Allow comments in PostScript-type functions. Change the TrueType font parser (FoFiTrueType) to delete glyf table entries that are too short. + +3.02-annot (2008-jul-15) +------------------------ +This patch implements editable annotations (currently +only freehand drawing) for xpdf. + +An annotation requires three components to be added +to the pdf file: + ++ the inklist, which contains point coordinates meant to + be used at 'selection' time; ++ the 'stream' contains the points to be plotted ++ the xref contains references to the objects + +The code in this patch +./xpdf/Annot.cc ../../work/xpdf-3.02/xpdf/Annot.cc +./xpdf/Annot.h ../../work/xpdf-3.02/xpdf/Annot.h + code to support inklist and selection + +./xpdf/GlobalParams.cc ../../work/xpdf-3.02/xpdf/GlobalParams.cc +./xpdf/GlobalParams.h ../../work/xpdf-3.02/xpdf/GlobalParams.h + new bindings for the keys, basically mouse release and delete + +./xpdf/PDFCore.cc ../../work/xpdf-3.02/xpdf/PDFCore.cc +./xpdf/PDFCore.h ../../work/xpdf-3.02/xpdf/PDFCore.h + + code to create the various pieces of the annotation. + There are three strings used to hold the 'annotation', + the stream and the 'appreareance' object, which are built + as the user moves the mouse with the left key pressed. + +./xpdf/PDFDoc.cc ../../work/xpdf-3.02/xpdf/PDFDoc.cc +./xpdf/PDFDoc.h ../../work/xpdf-3.02/xpdf/PDFDoc.h + Code to manipulate the document. + addTempAnnotation() adds the pieces of a new annotation + to an existing file, making a temporary copy if necessary; + + delTempAnnotation() removes an annotation, for simplicity + it does so by overwriting the blocks with whitespace so that + the xref does not change. + +./xpdf/Page.cc ../../work/xpdf-3.02/xpdf/Page.cc +./xpdf/Page.h ../../work/xpdf-3.02/xpdf/Page.h + some bits to support selection + +./xpdf/XPDFCore.cc ../../work/xpdf-3.02/xpdf/XPDFCore.cc +./xpdf/XPDFCore.h ../../work/xpdf-3.02/xpdf/XPDFCore.h + creation of the annotation, similar to PDFCore.cc + Also implement a dialog box. + +./xpdf/XPDFViewer.cc ../../work/xpdf-3.02/xpdf/XPDFViewer.cc +./xpdf/XPDFViewer.h ../../work/xpdf-3.02/xpdf/XPDFViewer.h + various GUI items, such as the box to select color and width, + and hooks to call the 'save' option when exiting. + +./xpdf/XRef.cc ../../work/xpdf-3.02/xpdf/XRef.cc +./xpdf/XRef.h ../../work/xpdf-3.02/xpdf/XRef.h + support for a 'numObjects' variable which may or may not differ + from 'size' + +./xpdf/about-text.h ../../work/xpdf-3.02/xpdf/about-text.h + menus + + diff -ur ./xpdf/Annot.cc ../../work/xpdf-3.02/xpdf/Annot.cc --- ./xpdf/Annot.cc 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/Annot.cc 2008-07-15 16:32:51.000000000 +0200 @@ -242,6 +242,10 @@ borderDash, borderDashLength, borderR, borderG, borderB); + //----- parse the inkList + + dict->lookup("InkList", &inkList); + //----- get the annotation appearance if (dict->lookup("AP", &apObj)->isDict()) { @@ -275,6 +279,7 @@ delete type; } appearance.free(); + inkList.free(); if (appearBuf) { delete appearBuf; } @@ -648,6 +653,61 @@ mkObj.free(); } +static void get_coord(Object &array, int pos, double &ret) { + Object coord; + array.arrayGet(pos, &coord); + if (coord.isReal()) { + ret = coord.getReal(); + } + coord.free(); +} + +/* return true if the point falls within the annotation */ +GBool Annot::selectAnnotation(double ux, double uy) { + int i, j; + + if (!inkList.isArray()) { + return gFalse; + } + // scan the array and see if the point we clicked is close to any + // segment connecting two points of the annotation + int ilength = inkList.arrayGetLength(); + + for (i = 0; i < ilength; i++) { + Object array; + if (!inkList.arrayGet(i, &array)->isArray()) + continue; + + int alength = array.arrayGetLength(); + double ox = -1, oy = -1; + for (j = 0; j < alength; j += 2) { + if (ox == -1 || oy == -1) { // first point + get_coord(array, j, ox); + get_coord(array, j + 1, oy); + continue; + } + double cx = ox, cy = oy; // default + get_coord(array, j, cx); + get_coord(array, j + 1, cy); + if (ox == cx && oy == cy) // same point, go on + continue; + // below, compute a rectangle around the segment and see if the + // point falls into the rectangle. + double dx = cx - ox; + double dy = cy - oy; + double l = sqrt(dx*dx + dy*dy); + double x = ((ux - ox)*dx + (uy - oy)*dy)/l; + double y = (-(ux - ox)*dy + (uy - oy)*dx)/l; + + if (x >= -2 && x<= l + 2 && y >= -4 && y <= 4) + return gTrue; + ox = cx; + oy = cy; + } + } + return gFalse; +} + // Set the current fill or stroke color, based on (which should // have 1, 3, or 4 elements). If is +1, color is brightened; // if is -1, color is darkened; otherwise color is not diff -ur ./xpdf/Annot.h ../../work/xpdf-3.02/xpdf/Annot.h --- ./xpdf/Annot.h 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/Annot.h 2008-07-15 16:32:51.000000000 +0200 @@ -72,6 +72,13 @@ // Get appearance object. Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); } + // Get annotation rectangle + void getRectangle(double *Xmin, double *Ymin, double *Xmax, double *Ymax) + { *Xmin = xMin; *Ymin = yMin; *Xmax = xMax; *Ymax = yMax; } + + // check if the given point is within the annotation rectangle + GBool selectAnnotation(double ux, double uy); + AnnotBorderStyle *getBorderStyle() { return borderStyle; } GBool match(Ref *refA) @@ -101,6 +108,7 @@ GString *type; // annotation type Object appearance; // a reference to the Form XObject stream // for the normal appearance + Object inkList; // a reference to the InkList array GString *appearBuf; double xMin, yMin, // annotation rectangle xMax, yMax; diff -ur ./xpdf/GlobalParams.cc ../../work/xpdf-3.02/xpdf/GlobalParams.cc --- ./xpdf/GlobalParams.cc 2008-07-11 10:44:27.000000000 +0200 +++ ../../work/xpdf-3.02/xpdf/GlobalParams.cc 2008-07-15 16:32:51.000000000 +0200 @@ -444,6 +444,17 @@ cmds->append(new GString(cmd1)); } +KeyBinding::KeyBinding(int codeA, int modsA, int contextA, + char *cmd0, char *cmd1, char *cmd2) { + code = codeA; + mods = modsA; + context = contextA; + cmds = new GList(); + cmds->append(new GString(cmd0)); + cmds->append(new GString(cmd1)); + cmds->append(new GString(cmd2)); +} + KeyBinding::KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA) { code = codeA; mods = modsA; @@ -792,10 +803,9 @@ //----- mouse buttons keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModNone, - xpdfKeyContextAny, "startSelection")); + xpdfKeyContextAny, "startDraw", "startSelection")); keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModNone, - xpdfKeyContextAny, "endSelection", - "followLinkNoSel")); + xpdfKeyContextAny, "endDraw", "endSelection", "followLinkNoSel")); keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress2, xpdfKeyModNone, xpdfKeyContextAny, "startPan")); keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease2, xpdfKeyModNone, @@ -828,7 +838,7 @@ keyBindings->append(new KeyBinding(xpdfKeyCodeBackspace, xpdfKeyModNone, xpdfKeyContextAny, "pageUp")); keyBindings->append(new KeyBinding(xpdfKeyCodeDelete, xpdfKeyModNone, - xpdfKeyContextAny, "pageUp")); + xpdfKeyContextAny, "deleteAnnotation")); keyBindings->append(new KeyBinding(xpdfKeyCodePgDn, xpdfKeyModNone, xpdfKeyContextAny, "pageDown")); keyBindings->append(new KeyBinding(' ', xpdfKeyModNone, diff -ur ./xpdf/GlobalParams.h ../../work/xpdf-3.02/xpdf/GlobalParams.h --- ./xpdf/GlobalParams.h 2008-07-11 10:44:27.000000000 +0200 +++ ../../work/xpdf-3.02/xpdf/GlobalParams.h 2008-07-15 16:32:51.000000000 +0200 @@ -137,6 +137,7 @@ KeyBinding(int codeA, int modsA, int contextA, char *cmd0); KeyBinding(int codeA, int modsA, int contextA, char *cmd0, char *cmd1); + KeyBinding(int codeA, int modsA, int contextA, char *cmd0, char *cmd1, char *cmd2); KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA); ~KeyBinding(); }; diff -ur ./xpdf/PDFCore.cc ../../work/xpdf-3.02/xpdf/PDFCore.cc --- ./xpdf/PDFCore.cc 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/PDFCore.cc 2008-07-15 16:32:51.000000000 +0200 @@ -84,6 +84,8 @@ int i; doc = NULL; + modified = gFalse; + temporary = gFalse; continuousMode = globalParams->getContinuousView(); drawAreaWidth = drawAreaHeight = 0; maxPageW = totalDocH = 0; @@ -91,12 +93,19 @@ topPage = 0; scrollX = scrollY = 0; zoom = defZoom; + color[0] = 0xff; color[1] = color[2] = 0x00; + thickness = 1; dpi = 0; rotate = 0; selectPage = 0; selectULX = selectLRX = 0; selectULY = selectLRY = 0; + + drawing = gFalse; + lastDrawnX = lastDrawnY = -1; + lastStreamX = lastStreamY = -1; + dragging = gFalse; lastDragLeft = lastDragTop = gTrue; @@ -370,6 +379,7 @@ double zoomA, int rotateA, GBool force, GBool addToHist) { double hDPI, vDPI, dpiA, uw, uh, ut; int w, h, t, x0, x1, y0, y1, x, y; + //double dx, dy; int rot; int pg0, pg1; PDFCoreTile *tile; @@ -857,6 +867,8 @@ GBool PDFCore::gotoNextPage(int inc, GBool top) { int pg, scrollYA; + if (modified) // or look at *doc->getAnnotationObject() + finalizeDraw(); if (!doc || doc->getNumPages() == 0 || topPage >= doc->getNumPages()) { return gFalse; } @@ -1491,6 +1503,145 @@ newSelectLRX, newSelectLRY); } +// finalize a pending draw command, by collating all pieces of info +// into the final file. +GBool PDFCore::finalizeDraw() { + int pg; + GString *s = *doc->getAnnotationObject(); + if (!s) + return gFalse; + // add the bounding box + s->appendf( + "]\n"\ + "/Rect[{0:.1f} {1:.1f} {2:.1f} {3:.1f}]\n", + rectXmin - thickness, rectYmin - thickness, rectXmax + thickness, rectYmax + thickness); + + *doc->getAppereanceObject() = GString::format( + "<<\n"\ + "/Matrix [1.0 0.0 0.0 1.0 {0:.1f} {1:.1f}]\n"\ + "/Subtype /Form\n"\ + "/Length {2:d}\n"\ + "/FormType 1\n"\ + "/Type /XObject\n"\ + "/BBox[{3:.1f} {4:.1f} {5:.1f} {6:.1f}]\n"\ + ">>\n"\ + "stream\n"\ + "{7:t}"\ + "endstream\n"\ + "endobject\n", + -rectXmin + thickness, -rectYmin + thickness, + (*doc->getAppereanceStream())->getLength(), + rectXmin - thickness, rectYmin - thickness, rectXmax + thickness, rectYmax + thickness, + (*doc->getAppereanceStream())); + + pg = topPage; + doc->addTempAnnotation(pg, getTemporary()); + GString *temp = doc->getFileName()->copy(); + if (getTemporary() == gFalse) + temp->append(".temp"); + setTemporary(gTrue); + setModified(gFalse); + loadFile(temp); + displayPage(pg, getZoom(), getRotate(), gFalse, gFalse); + delete temp; + return gTrue; +} + +/* draw the next point */ +void PDFCore::drawPoint(int pg, int x, int y) { + //int newSelectULX, newSelectULY, newSelectLRX, newSelectLRY; + GBool haveSel; + SplashColor xorColor; + PDFCorePage *page; + SplashPattern *pattern; + int i; + + haveSel = selectULX != selectLRX && selectULY != selectLRY; + + if (haveSel) { + xorColor[0] = xorColor[1] = xorColor[2] = 0xff; + xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY, + new SplashSolidColor(xorColor)); + } + page = findPage(pg); + if (!page) + return; + if (haveSel) { + redrawWindow(page->xDest + selectULX, page->yDest + selectULY, + selectLRX - selectULX + 1, selectLRY - selectULY + 1, gFalse); + } + selectULX = selectLRX = selectULY = selectLRY = 0; + xorColor[0] = color[0]; + xorColor[1] = color[1]; + xorColor[2] = color[2]; + pattern = new SplashSolidColor(xorColor); + + // The page can have multiple tiles, so we work on each of them. + // In practice, most of the times we have only one tile. + for (i = 0; i < page->tiles->getLength(); ++i) { + int xi = 0, yi = 0, wi = 0, hi = 0; + PDFCoreTile *tile = (PDFCoreTile *)page->tiles->get(i); + Splash *splash = new Splash(tile->bitmap, gFalse); + + splash->setFillPattern(pattern->copy()); + if (lastDrawnX != -1 && lastDrawnY != -1) { + // draw a line from the previous point (lastDrawnX, lastDrawnY) + // to the current one. + SplashCoord xx0 = (SplashCoord)(lastDrawnX - tile->xMin); + SplashCoord yy0 = (SplashCoord)(lastDrawnY - tile->yMin); + SplashCoord xx1 = (SplashCoord)(x - tile->xMin); + SplashCoord yy1 = (SplashCoord)(y - tile->yMin); + SplashPath *path = new SplashPath(); + path->moveTo(xx0, yy0); + path->lineTo(xx1, yy1); + path->close(); + splash->fill(path, gTrue); + delete path; + delete splash; + + // the following code computes the size of the region to redraw + // which goes between min(x, lastDrawnX) and max(...) + // on both coordinates. + // Also if the region is outside the tile, we truncate it. + if (x < lastDrawnX) { + xi = x - tile->xMin; + wi = lastDrawnX - x; + } else { + xi = lastDrawnX - tile->xMin; + wi = x - lastDrawnX; + } + if (xi < 0) { + wi += xi; + xi = 0; + } + if (xi + wi > tile->bitmap->getWidth()) { + wi = tile->bitmap->getWidth() - xi; + } + if (y < lastDrawnY) { + yi = y - tile->yMin; + hi = lastDrawnY - y; + } else { + yi = lastDrawnY - tile->yMin; + hi = y - lastDrawnY; + } + if (yi < 0) { + hi += yi; + yi = 0; + } + if (yi + hi > tile->bitmap->getHeight()) { + hi = tile->bitmap->getHeight() - yi; + } + wi = wi ? wi : 1; // make sure size is at least 1 pixel + hi = hi ? hi : 1; + updateTileData(tile, xi, yi, wi, hi, gTrue); + redrawWindow(page->xDest + xi, page->yDest + yi, wi, hi, gFalse); + } + lastDrawnX = x; + lastDrawnY = y; + } + delete pattern; +} + void PDFCore::xorRectangle(int pg, int x0, int y0, int x1, int y1, SplashPattern *pattern, PDFCoreTile *oneTile) { Splash *splash; diff -ur ./xpdf/PDFCore.h ../../work/xpdf-3.02/xpdf/PDFCore.h --- ./xpdf/PDFCore.h 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/PDFCore.h 2008-07-15 16:32:51.000000000 +0200 @@ -200,6 +200,10 @@ GBool getSelection(int *pg, double *ulx, double *uly, double *lrx, double *lry); + // Note drawing + GBool finalizeDraw(); + void drawPoint(int pg, int x, int y); + // Text extraction. GString *extractText(int pg, double xMin, double yMin, double xMax, double yMax); @@ -231,6 +235,11 @@ int getPageNum() { return topPage; } double getZoom() { return zoom; } double getZoomDPI() { return dpi; } + void setColor(Guchar R, Guchar G, Guchar B) { + color[0] = R; color[1] = G; color[2] = B; + } + void setThickness(int value) { thickness = value; } + int getRotate() { return rotate; } GBool getContinuousMode() { return continuousMode; } virtual void setReverseVideo(GBool reverseVideoA); @@ -242,6 +251,10 @@ int getDrawAreaHeight() { return drawAreaHeight; } virtual void setBusyCursor(GBool busy) = 0; LinkAction *findLink(int pg, double x, double y); + GBool getModified() { return modified; } + void setModified(GBool mod) { modified = mod; } + GBool getTemporary() { return temporary; } + void setTemporary(GBool mod) { temporary = mod; } protected: @@ -271,6 +284,9 @@ virtual GBool checkForNewFile() { return gFalse; } PDFDoc *doc; // current PDF file + GBool modified; // set if the document need to be saved + // before quitting + GBool temporary; // set if the current document is temporary GBool continuousMode; // false for single-page mode, true for // continuous mode int drawAreaWidth, // size of the PDF display area @@ -289,12 +305,30 @@ double zoom; // current zoom level, in percent of 72 dpi double dpi; // current zoom level, in DPI int rotate; // current page rotation + Guchar color[3]; // current color used for drawing annotations + int thickness; // current thickness used for drawing annotations int selectPage; // page number of current selection int selectULX, // coordinates of current selection, selectULY, // in device space -- (ULX==LRX || ULY==LRY) selectLRX, // means there is no selection selectLRY; + + GBool drawing; // set while drawing is happening; everytime we release + // the mouse button, drawing is reset + int lastDrawnX, // coordinates of the last drawed point: used for rendering + lastDrawnY; // on the screen + double lastStreamX, // coordinates of the last stored point inside the + lastStreamY; // temporary file of the annotation stream + int streamSize; + + int numDrawnPoints; // counter for the points added to the temp stream; + + double rectXmin, // coordinates of the bounding box for the annotation + rectYmin, // we are drawing at execution time + rectXmax, + rectYmax; + GBool dragging; // set while selection is being dragged GBool lastDragLeft; // last dragged selection edge was left/right GBool lastDragTop; // last dragged selection edge was top/bottom diff -ur ./xpdf/PDFDoc.cc ../../work/xpdf-3.02/xpdf/PDFDoc.cc --- ./xpdf/PDFDoc.cc 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/PDFDoc.cc 2008-07-15 16:32:51.000000000 +0200 @@ -64,6 +64,7 @@ #ifndef DISABLE_OUTLINE outline = NULL; #endif + annotationObject = appereanceObject = appereanceStream = NULL; fileName = fileNameA; fileName1 = fileName; @@ -121,6 +122,7 @@ #ifndef DISABLE_OUTLINE outline = NULL; #endif + annotationObject = appereanceObject = appereanceStream = NULL; //~ file name should be stored in Unicode (?) fileName = new GString(); @@ -174,6 +176,7 @@ #ifndef DISABLE_OUTLINE outline = NULL; #endif + annotationObject = appereanceObject = appereanceStream = NULL; ok = setup(ownerPassword, userPassword); } @@ -400,3 +403,324 @@ fclose(f); return gTrue; } + +// copy data from str to tempFp. If len == -1 copy to the end +// of file, otherwise copy the specified number of bytes. +static int stream_copy(BaseStream *str, FILE *tempFp, int len) { + int i; + for (i = 0; len == -1 || i < len; i++) { + int c = str->getChar(); + if (c == EOF) + break; + fputc(c, tempFp); + } + return i; // number of bytes written +} + +// make a copy of the stream into a temp file, +// increase len by the amount of bytes written +static FILE *make_temp_copy(GString *fileName, BaseStream *str, int *len) +{ + int dummy = 0; + if (len == NULL) + len = &dummy; + GString *tempFileName = fileName->copy()->append(".temp"); + FILE *tempFp = fopen(tempFileName->getCString(), "wb"); + delete tempFileName; + if (tempFp == NULL) /*ERROR*/ + return NULL; + str->reset(); + *len += stream_copy(str, tempFp, -1); // copy to EOF + str->close(); + return tempFp; +} + +// called when the user closes an annotation so it is saved. +// We need to make a temporary copy, and append the various pieces. +GString *PDFDoc::addTempAnnotation(int pg, GBool temporary) { + int written = 0, c, + offPage = 0, + offAnnArr = 0, + offAnn, + offAp, start, end, numObjects = xref->getNumObjects(); + + GString *tempFileName; + // make a temporary copy of the whole file. + FILE *tempFp = make_temp_copy(fileName, str, &written); + if (tempFp == NULL) //ERROR + return NULL; + + Page *page = catalog->getPage(pg); + Object obj; + Annots *annotList = new Annots(xref, catalog, page->getAnnots(&obj)); + obj.free(); + Ref *ref = catalog->getPageRef(pg); + XRefEntry *pageRef = xref->getEntry(ref->num); + str->reset(); + Lexer *lexer = new Lexer(xref, str); + + if (annotList->getNumAnnots() == 0) { + // there are no annotations yet. + offPage = written; + lexer->setPos(pageRef->offset); + start = lexer->getPos(); + lexer->getObj(&obj); // get page number + obj.free(); + lexer->getObj(&obj); // get page gen + obj.free(); + lexer->getObj(&obj); // get "obj" + obj.free(); + lexer->getObj(&obj); // get begin of dictionary + obj.free(); + + end = lexer->getPos(); + lexer->setPos(start); + written += stream_copy(str, tempFp, end - start); + start = end; + written += fprintf(tempFp, " /Annots [ %d 0 R ]", numObjects); + } else { + // we do have an annotation, either object or inline. + // Locate the beginning of the array and add the new entry there. + if (page->getAnnotNum() != -1) { // annotation object, jump there. + offAnnArr = written; + lexer->setPos(xref->getEntry(page->getAnnotNum())->offset); + start = lexer->getPos(); + lexer->getObj(&obj); // get annotation array number + obj.free(); + lexer->getObj(&obj); // get annotation array gen + obj.free(); + lexer->getObj(&obj); // get "obj" + obj.free(); + lexer->getObj(&obj); // get begin of array "[" + obj.free(); + } else { + // the annotation is inline. Again, move to the beginning of + // the object and write the new annotation in the first position. + offPage = written; + lexer->setPos(pageRef->offset); + start = lexer->getPos(); + while (1) { + lexer->getObj(&obj); + if (obj.isName() && !strncmp("Annots", obj.getName(), 6)) { + obj.free(); + lexer->getObj(&obj); // begin of array "[" + obj.free(); + break; + } + obj.free(); + } + } + // common part to update an existing annotation. + end = lexer->getPos(); + lexer->setPos(start); + written += stream_copy(str, tempFp, end - start); + start = end; + written += fprintf(tempFp, " %d 0 R ", numObjects); + } + // copy till "endobj" + while (1) { + lexer->getObj(&obj); + if (obj.isCmd() && !strncmp("endobj", obj.getCmd(), 6)) { + obj.free(); + break; + } + obj.free(); + } + end = lexer->getPos(); + + lexer->setPos(start); + written += stream_copy(str, tempFp, end - start); + fputc('\n', tempFp); + written++; + // Add annotation object + offAnn = written; + written += fprintf(tempFp, "%d 0 obj\n", numObjects); + written += fprintf(tempFp, "%s", annotationObject->getCString()); + written += fprintf(tempFp, "/AP\n<<\n/N %d 0 R\n>>\n>>\nendobj\n", numObjects + 1); + numObjects++; + + // Add appereance object + offAp = written; + written += fprintf(tempFp, "%d 0 obj\n", numObjects); + written += fprintf(tempFp, "%s", appereanceObject->getCString()); + delete annotationObject; + delete appereanceObject; + delete appereanceStream; + /* XREF */ + + int the_ref; // reference to put in the xref + int the_offset; // offset to put in the xref + if (annotList->getNumAnnots() == 0) { // no pre-existing annotation + the_ref = ref->num; + the_offset = offPage; + } else if (page->getAnnotNum() != -1) { + // preexisting annotation array + the_ref = page->getAnnotNum(); + the_offset = offAnnArr; + } else { + // inline annotation + the_ref = ref->num; + the_offset = offPage; + } + fprintf(tempFp, + "xref\n"\ + "%d 1\n"\ + "%010d 00000 n \n"\ + "%d 2\n"\ + "%010d 00000 n \n"\ + "%010d 00000 n \n"\ + "trailer\n"\ + "<<\n"\ + "/Size %d\n"\ + "/Prev %d\n"\ + "/Root %d %d R\n"\ + ">>\n"\ + "startxref\n"\ + "%d\n"\ + "%%%%EOF\n", + the_ref, the_offset, + numObjects - 1, offAnn, offAp, + numObjects + 1, xref->getLastXRefPos(), + xref->getRootNum(), xref->getRootGen(), written); + // XXX keep the space above after the 'n', see pg.94 pdf reference 1.7 + delete annotList; + fclose(tempFp); + str->close(); + + if (temporary) { + tempFileName = fileName->copy(); + rename(tempFileName->append(".temp")->getCString(), + fileName->getCString()); + delete tempFileName; + } + return NULL; +} + +// write len spaces to the file, used to overwrite 'deleted' data +static void write_spaces(FILE *fp, int len) { + int i; + for (i=0; i < len; i++) + fputc(' ', fp); +} + +// remove annotation n on page pg +void PDFDoc::delTempAnnotation(int pg, int n, GBool temporary) { + XRefEntry *target; + Page *page; + Object obj; + GString *tempFileName; + Lexer *lexer; + FILE *tempFp; + int i, start, end; + + tempFp = make_temp_copy(fileName, str, NULL); + if (tempFp == NULL) { /*ERROR*/ + return; + } + + str->reset(); + lexer = new Lexer(xref, str); + page = catalog->getPage(pg); + if (page->getAnnotNum() != -1) { // annotation in the array + target = xref->getEntry(page->getAnnotNum()); + lexer->setPos(target->offset); + lexer->getObj(&obj); // - annotation array number + obj.free(); + lexer->getObj(&obj); // - annotation array gen + obj.free(); + lexer->getObj(&obj); // - "obj" string + obj.free(); + } else { // inline annotation + target = xref->getEntry(catalog->getPageRef(pg)->num); + lexer->setPos(target->offset); + // locate the Annots object + while(1) { + lexer->getObj(&obj); + if (obj.isName() && !strncmp("Annots", obj.getName(), 6)) { + obj.free(); + break; + } + obj.free(); + } + } + lexer->getObj(&obj); // - "[" begin of array + obj.free(); + // skip the first n-1 annotations, jump to target annotation + for (i = 0; i < n; i++) { + lexer->getObj(&obj); // annotation number + obj.free(); + lexer->getObj(&obj); // annotation gen + obj.free(); + lexer->getObj(&obj); // "R" + obj.free(); + } + + // compute the width of the target annotation + start = lexer->getPos(); + lexer->getObj(&obj); // annotation number + n = obj.getInt(); + obj.free(); + lexer->getObj(&obj); // annotation gen + obj.free(); + lexer->getObj(&obj); // "R" + obj.free(); + end = lexer->getPos(); + + // overwrite data with spaces + fseek(tempFp, start, SEEK_SET); + write_spaces(tempFp, end - start); + + // now locate and compute size of the annotation object + target = xref->getEntry(n); + lexer->setPos(target->offset); + start = target->offset; + while (1) { + lexer->getObj(&obj); + if (obj.isName() && !strncmp("N", obj.getName(), 1)) { + obj.free(); + lexer->getObj(&obj); + n = obj.getInt(); + } else if (obj.isCmd() && !strncmp("endobj", obj.getCmd(), 6)) { + obj.free(); + break; + } + obj.free(); + } + end = lexer->getPos(); + + fseek(tempFp, start, SEEK_SET); + write_spaces(tempFp, end - start); + + // and finally do the same for the stream + target = xref->getEntry(n); + lexer->setPos(target->offset); + start = target->offset; + while (1) { + lexer->getObj(&obj); + if (obj.isName() && (strncmp("Length", obj.getName(), 6) == 0)) { + obj.free(); + lexer->getObj(&obj); + n = obj.getInt(); + } else if (obj.isCmd() && (strncmp("stream", obj.getCmd(), 6) == 0)) { + lexer->setPos(lexer->getPos() + n); + } else if (obj.isCmd() && (strncmp("endobj", obj.getCmd(), 6) == 0)) { + obj.free(); + break; + } + obj.free(); + } + end = lexer->getPos(); + + fseek(tempFp, start, SEEK_SET); + write_spaces(tempFp, end - start); // overwrite data with spaces + + str->close(); + fclose(tempFp); + + if (temporary) { + tempFileName = fileName->copy(); + rename(tempFileName->append(".temp")->getCString(), + fileName->getCString()); + delete tempFileName; + } +} diff -ur ./xpdf/PDFDoc.h ../../work/xpdf-3.02/xpdf/PDFDoc.h --- ./xpdf/PDFDoc.h 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/PDFDoc.h 2008-07-15 16:32:51.000000000 +0200 @@ -154,10 +154,17 @@ // Save this file with another name. GBool saveAs(GString *name); + // Save file with added annotations + GString *addTempAnnotation(int pg, GBool temporary); + void delTempAnnotation(int pg, int n, GBool temporary); + // Misc access for temporary annotations + GString **getAnnotationObject() { return &annotationObject; } + GString **getAppereanceObject() { return &appereanceObject; } + GString **getAppereanceStream() { return &appereanceStream; } + // Return a pointer to the GUI (XPDFCore or WinPDFCore object). void *getGUIData() { return guiData; } - private: GBool setup(GString *ownerPassword, GString *userPassword); @@ -175,6 +182,9 @@ Outline *outline; #endif + GString *annotationObject, // local buffers used during creation of + *appereanceObject, // temporary annotations + *appereanceStream; GBool ok; int errCode; diff -ur ./xpdf/Page.cc ../../work/xpdf-3.02/xpdf/Page.cc --- ./xpdf/Page.cc 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/Page.cc 2008-07-15 16:32:51.000000000 +0200 @@ -221,6 +221,13 @@ annots.free(); goto err2; } + if (annots.isRef()) { + annotNum = annots.getRef().num; + annotGen = annots.getRef().gen; + } else { + annotNum = annotGen = -1; + } + selectedAnnotation = -1; // contents pageDict->lookupNF("Contents", &contents); @@ -261,6 +268,7 @@ GBool printing, Catalog *catalog, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData) { + setSelectedAnnotation(-1); displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop, -1, -1, -1, -1, printing, catalog, abortCheckCbk, abortCheckCbkData); diff -ur ./xpdf/Page.h ../../work/xpdf-3.02/xpdf/Page.h --- ./xpdf/Page.h 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/Page.h 2008-07-15 16:32:51.000000000 +0200 @@ -16,6 +16,8 @@ #endif #include "Object.h" +#include "GfxState.h" +#include "Annot.h" class Dict; class XRef; @@ -142,6 +144,12 @@ // Get annotations array. Object *getAnnots(Object *obj) { return annots.fetch(xref, obj); } + // Get annotations information + int getAnnotNum() { return annotNum; } + int getAnnotGen() { return annotGen; } + int getSelectedAnnotation () { return selectedAnnotation; } + void setSelectedAnnotation (int i) { selectedAnnotation = i; } + // Return a list of links. Links *getLinks(Catalog *catalog); @@ -180,6 +188,9 @@ int num; // page number PageAttrs *attrs; // page attributes Object annots; // annotations array + int annotNum, + annotGen, + selectedAnnotation; Object contents; // page contents GBool ok; // true if page is valid }; diff -ur ./xpdf/XPDFCore.cc ../../work/xpdf-3.02/xpdf/XPDFCore.cc --- ./xpdf/XPDFCore.cc 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/XPDFCore.cc 2008-07-15 16:32:51.000000000 +0200 @@ -29,6 +29,7 @@ #include "TextOutputDev.h" #include "SplashBitmap.h" #include "SplashPattern.h" +#include "Annot.h" #include "XPDFApp.h" #include "XPDFCore.h" @@ -140,6 +141,8 @@ hyperlinksEnabled = gTrue; selectEnabled = gTrue; + drawEnabled = gFalse; + // do X-specific initialization and create the widgets initWindow(); initPasswordDialog(); @@ -166,6 +169,9 @@ if (selectCursor) { XFreeCursor(display, selectCursor); } + if (drawCursor) { + XFreeCursor(display, drawCursor); + } } //------------------------------------------------------------------------ @@ -353,14 +359,43 @@ //------------------------------------------------------------------------ // selection //------------------------------------------------------------------------ - void XPDFCore::startSelection(int wx, int wy) { int pg, x, y; + double Xmin, Ymin, Xmax, Ymax, ux, uy; + int dxmin, dymin, dxmax, dymax; takeFocus(); if (doc && doc->getNumPages() > 0) { - if (selectEnabled) { - if (cvtWindowToDev(wx, wy, &pg, &x, &y)) { + if (cvtWindowToDev(wx, wy, &pg, &x, &y)) { + if (selectEnabled && !drawEnabled) { + // this block is used to check if we are trying + // to select an annotation + XRef *xref = doc->getXRef(); + Catalog *cat = doc->getCatalog(); + + //printf("%d\n", xref->getNumObjects()); + Page *page = cat->getPage(pg); + Object obj; + Annots *annotList = new Annots(xref, cat, page->getAnnots(&obj)); + + obj.free(); + int i, length = annotList->getNumAnnots(); + for (i = 0; i < length; ++i) { + Annot *annot = annotList->getAnnot(i); + annot->getRectangle(&Xmin, &Ymin, &Xmax, &Ymax); + cvtUserToDev(pg, Xmin, Ymin, &dxmin, &dymax); + cvtUserToDev(pg, Xmax, Ymax, &dxmax, &dymin); + cvtWindowToUser(wx, wy, &pg, &ux, &uy); + if (annot->selectAnnotation(ux, uy)) { + page->setSelectedAnnotation(i); + setSelection(pg, dxmin, dymin, dxmax, dymax); + delete annotList; + return; + } + } + delete annotList; + // otherwise, we start a selection + page->setSelectedAnnotation(-1); setSelection(pg, x, y, x, y); setCursor(selectCursor); dragging = gTrue; @@ -379,7 +414,8 @@ dragging = gFalse; setCursor(None); if (ok) { - moveSelection(pg, x, y); + if (selectEnabled && !drawEnabled) + moveSelection(pg, x, y); } #ifndef NO_TEXT_SELECT if (selectULX != selectLRX && @@ -454,6 +490,151 @@ } //------------------------------------------------------------------------ +// annotations +//------------------------------------------------------------------------ +// called when pressing the 'draw' button, creates the header for +// the annotation and the stream. +void XPDFCore::initializeDraw(int pg, int x, int y) { + double dx, dy; + double r, g, b; + + GString **ann = doc->getAnnotationObject(); + if (*ann) // annotation already started. + return; + r = 1.0*(Guint)color[0]/(Guint)0xff; + g = 1.0*(Guint)color[1]/(Guint)0xff; + b = 1.0*(Guint)color[2]/(Guint)0xff; + + cvtDevToUser(pg, x, y, &dx, &dy); + *ann = GString::format( + "<<\n"\ + "/Type/Annot\n"\ + "/Subtype/Ink\n"\ + "/C[{0:.1f} {1:.1f} {2:.1f}]\n"\ + "/Subject(Pencil)\n"\ + "/BS <>\n"\ + "/InkList[", r, g, b, thickness); + + *doc->getAppereanceStream() = GString::format( + "{0:.1f} {1:.1f} {2:.1f} RG\n{3:d} w\n", r, g, b, thickness); + + numDrawnPoints = 0; + rectXmin = rectXmax = dx; + rectYmin = rectYmax = dy; +} + +// the actual callback on a start Draw +void XPDFCore::startDraw(int wx, int wy) { + int pg, x, y; + + lastDrawnX = lastDrawnY = -1; + takeFocus(); + if (!doc || doc->getNumPages() == 0 || !drawEnabled || + !cvtWindowToDev(wx, wy, &pg, &x, &y)) + return; + initializeDraw(pg, x, y); + modified = gTrue; // or look at *doc->getAnnotationObject() + continueDraw(pg, x, y, 0); + drawing = gTrue; + drawPoint(pg, x, y); + setCursor(drawCursor); +} + +// flag=0 on the first point, 1 on intermediate, 2 on the last one. +// add an element to the annotation and also to the stream +void XPDFCore::continueDraw(int pg, int x, int y, int flag) { + GString *s = *doc->getAnnotationObject(); + double dx, dy; + + cvtDevToUser(pg, x, y, &dx, &dy); + if (flag == 0) + s->append("["); + s->appendf("{0:.1f} {1:.1f} ", dx, dy); + if (flag == 2) + s->append("]"); + + s = *doc->getAppereanceStream(); + if (flag == 0) { // first point, store unconditionally + s->appendf("{0:.1f} {1:.1f} m\n", dx, dy); + numDrawnPoints++; + lastStreamX = dx; + lastStreamY = dy; + } else if (flag == 1) { // other point + // compute distance, ignore if too close + double delta = (lastStreamX - dx)*(lastStreamX - dx) + + (lastStreamY - dy)*(lastStreamY - dy); + if (delta > 49) { + s->appendf("{0:.1f} {1:.1f} ", dx, dy); + numDrawnPoints++; + if (numDrawnPoints%3 == 1) + s->append("c\n"); + lastStreamX = dx; + lastStreamY = dy; + } + } else { // XXX final point. We do not plot the point itself, + // and close the bezier curve in a sensible way depending on + // the number of points previously printed. + if (numDrawnPoints%3 == 2) + s->append("l\nS\n"); + else if (numDrawnPoints%3 == 0) + s->append("v\nS\n"); + else + s->append("S\n"); + numDrawnPoints = 0; + } + + // update the coordinates of the bounding box + if (dx < rectXmin) + rectXmin = dx; + if (dx > rectXmax) + rectXmax = dx; + if (dy < rectYmin) + rectYmin = dy; + if (dy > rectYmax) + rectYmax = dy; +} + +// should be similar to endSelection +void XPDFCore::endDraw(int wx, int wy) { + int pg, x, y; + GBool ok; + + ok = cvtWindowToDev(wx, wy, &pg, &x, &y); + if (drawEnabled) { + drawing = gFalse; + setCursor(None); + if (ok) { + drawPoint(pg, x, y); + } else { // outside the window, reuse the last point + x = lastDrawnX; + y = lastDrawnY; + } + continueDraw(pg, x, y, 2); + } +} + +// handler when deleting an annotation: make a temp copy, +// overwrite the object with whitespace, and reload. +void XPDFCore::deleteAnnotation() { + Catalog *cat = doc->getCatalog(); + if (!cat) + return; + Page *page = cat->getPage(topPage); + if (!page || page->getSelectedAnnotation() == -1) + return; + + int pg = topPage; + doc->delTempAnnotation(pg, page->getSelectedAnnotation(), getTemporary()); + GString *temp = doc->getFileName()->copy(); + if (getTemporary() == gFalse) + temp->append(".temp"); + setTemporary(gTrue); + loadFile(temp); + displayPage(pg, getZoom(), getRotate(), gFalse, gFalse); + return; +} + +//------------------------------------------------------------------------ // hyperlinks //------------------------------------------------------------------------ @@ -883,6 +1064,7 @@ busyCursor = XCreateFontCursor(display, XC_watch); linkCursor = XCreateFontCursor(display, XC_hand2); selectCursor = XCreateFontCursor(display, XC_cross); + drawCursor = XCreateFontCursor(display, XC_pencil); currentCursor = 0; // create the scrolled window and scrollbars @@ -1083,7 +1265,16 @@ &pg, &x, &y); if (core->dragging) { if (ok) { - core->moveSelection(pg, x, y); + if (core->selectEnabled) { + core->moveSelection(pg, x, y); + } + } + } else if (core->drawing) { + if (ok) { + if (core->drawEnabled) { + core->continueDraw(pg, x, y, 1); + core->drawPoint(pg, x, y); + } } } else if (core->hyperlinksEnabled) { core->cvtDevToUser(pg, x, y, &xu, &yu); @@ -1500,6 +1691,68 @@ core->dialogDone = -1; } +GBool XPDFCore::doPromptDialog(char *title, GString *msg) { + Widget dialog; + XtAppContext appContext; + Arg args[20]; + int n; + XmString s1, s2, s3; + XEvent event; + + n = 0; + XtSetArg(args[n], XmNdialogType, XmDIALOG_PROMPT); ++n; + XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n; + s1 = XmStringCreateLocalized(title); + XtSetArg(args[n], XmNdialogTitle, s1); ++n; + s2 = XmStringCreateLocalized(msg->getCString()); + XtSetArg(args[n], XmNselectionLabelString, s2); ++n; + s3 = XmStringCreateLocalized(""); + XtSetArg(args[n], XmNtextString, s3); ++n; + dialog = XmCreatePromptDialog(drawArea, "promptDialog", args, n); + XmStringFree(s1); + XmStringFree(s2); + XmStringFree(s3); + XtManageChild(dialog); + + XtAddCallback(dialog, XmNokCallback, + &dialogPromptOkCbk, (XtPointer)this); + XtAddCallback(dialog, XmNcancelCallback, + &dialogPromptCancelCbk, (XtPointer)this); + + appContext = XtWidgetToApplicationContext(dialog); + dialogDone = 0; + do { + XtAppNextEvent(appContext, &event); + XtDispatchEvent(&event); + } while (!dialogDone); + + XtUnmanageChild(dialog); + XtDestroyWidget(dialog); + + return dialogDone > 0; +} + +void XPDFCore::dialogPromptOkCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XmSelectionBoxCallbackStruct *selection = + (XmSelectionBoxCallbackStruct *)callData; + char *str = (char *)XmStringUnparse(selection->value, 0, XmCHARSET_TEXT, XmCHARSET_TEXT, 0, 0, XmOUTPUT_ALL); + printf("%s\n", (str && str[0]) ? str : "(NULL)"); + XtFree(str); + core->dialogDone = 1; +} + +void XPDFCore::dialogPromptCancelCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFCore *core = (XPDFCore *)ptr; + XmSelectionBoxCallbackStruct *selection = + (XmSelectionBoxCallbackStruct *)callData; + printf("(NULL)\n"); + core->dialogDone = -1; +} + + //------------------------------------------------------------------------ // password dialog //------------------------------------------------------------------------ diff -ur ./xpdf/XPDFCore.h ../../work/xpdf-3.02/xpdf/XPDFCore.h --- ./xpdf/XPDFCore.h 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/XPDFCore.h 2008-07-15 16:32:51.000000000 +0200 @@ -98,6 +98,13 @@ void startPan(int wx, int wy); void endPan(int wx, int wy); + //----- annotations + void initializeDraw(int pg, int x, int y); + void startDraw(int wx, int wy); + void continueDraw(int pg, int x, int y, int flag); + void endDraw(int wx, int wy); + void deleteAnnotation(); + //----- hyperlinks void doAction(LinkAction *action); @@ -116,6 +123,7 @@ GBool doQuestionDialog(char *title, GString *msg); void doInfoDialog(char *title, GString *msg); void doErrorDialog(char *title, GString *msg); + GBool doPromptDialog(char *title, GString *msg); //----- password dialog @@ -128,6 +136,8 @@ virtual void setBusyCursor(GBool busy); Cursor getBusyCursor() { return busyCursor; } void takeFocus(); + void enableDraw(GBool on) { drawEnabled = on; } + GBool getDrawEnabled() { return drawEnabled; } void enableHyperlinks(GBool on) { hyperlinksEnabled = on; } GBool getHyperlinksEnabled() { return hyperlinksEnabled; } void enableSelect(GBool on) { selectEnabled = on; } @@ -189,6 +199,10 @@ XtPointer callData); static void passwordCancelCbk(Widget widget, XtPointer ptr, XtPointer callData); + static void dialogPromptOkCbk(Widget widget, XtPointer ptr, + XtPointer callData); + static void dialogPromptCancelCbk(Widget widget, XtPointer ptr, + XtPointer callData); Gulong paperPixel; Gulong mattePixel; @@ -214,7 +228,7 @@ Widget vScrollBar; Widget drawAreaFrame; Widget drawArea; - Cursor busyCursor, linkCursor, selectCursor; + Cursor busyCursor, linkCursor, selectCursor, drawCursor; Cursor currentCursor; GC drawAreaGC; // GC for blitting into drawArea @@ -238,6 +252,7 @@ XPDFMouseCbk mouseCbk; void *mouseCbkData; + GBool drawEnabled; //flag indicating whether we are drawing or not GBool hyperlinksEnabled; GBool selectEnabled; diff -ur ./xpdf/XPDFViewer.cc ../../work/xpdf-3.02/xpdf/XPDFViewer.cc --- ./xpdf/XPDFViewer.cc 2008-07-11 10:44:28.000000000 +0200 +++ ../../work/xpdf-3.02/xpdf/XPDFViewer.cc 2008-07-15 16:32:51.000000000 +0200 @@ -151,6 +151,43 @@ { "fit width", zoomWidth } }; +struct ThicknessMenuInfo { + char *label; + int value; +}; + +static ThicknessMenuInfo thicknessMenuInfo[] = { + { " 1" , 1 }, + { " 2" , 2 }, + { " 3" , 3 }, + { " 4" , 4 }, + { " 5" , 5 }, + { " 6" , 6 }, + { " 7" , 7 }, + { " 8" , 8 }, + { " 9" , 9 } +}; +#define nThicknessMenuItems (sizeof(thicknessMenuInfo)/sizeof(ThicknessMenuInfo)) + +struct ColorMenuInfo { + char* label; + Guchar R; + Guchar G; + Guchar B; +}; + +static ColorMenuInfo colorMenuInfo[] = { + {"Black", 0x00, 0x00, 0x00}, + {"Blue", 0x00, 0x00, 0xff}, + {"Green", 0x00, 0xff, 0x00}, + {"Cyan", 0x00, 0xff, 0xff}, + {"Red", 0xff, 0x00, 0x00}, + {"Magenta", 0xff, 0x00, 0xff}, + {"Yellow", 0xff, 0xff, 0x00}, + {"White", 0xff, 0xff, 0xff} +}; +#define nColorMenuItems (sizeof(colorMenuInfo)/sizeof(ColorMenuInfo)) + #define maxZoomIdx 0 #define defZoomIdx 3 #define minZoomIdx 7 @@ -166,6 +203,8 @@ { "closeOutline", 0, gFalse, gFalse, &XPDFViewer::cmdCloseOutline }, { "closeWindow", 0, gFalse, gFalse, &XPDFViewer::cmdCloseWindow }, { "continuousMode", 0, gFalse, gFalse, &XPDFViewer::cmdContinuousMode }, + { "deleteAnnotation", 0, gTrue, gTrue, &XPDFViewer::cmdDeleteAnnotation }, + { "endDraw", 0, gTrue, gTrue, &XPDFViewer::cmdEndDraw}, { "endPan", 0, gTrue, gTrue, &XPDFViewer::cmdEndPan }, { "endSelection", 0, gTrue, gTrue, &XPDFViewer::cmdEndSelection }, { "find", 0, gTrue, gFalse, &XPDFViewer::cmdFind }, @@ -221,6 +260,7 @@ { "scrollUp", 1, gTrue, gFalse, &XPDFViewer::cmdScrollUp }, { "scrollUpPrevPage", 1, gTrue, gFalse, &XPDFViewer::cmdScrollUpPrevPage }, { "singlePageMode", 0, gFalse, gFalse, &XPDFViewer::cmdSinglePageMode }, + { "startDraw", 0, gTrue, gTrue, &XPDFViewer::cmdStartDraw }, { "startPan", 0, gTrue, gTrue, &XPDFViewer::cmdStartPan }, { "startSelection", 0, gTrue, gTrue, &XPDFViewer::cmdStartSelection }, { "toggleContinuousMode", 0, gFalse, gFalse, &XPDFViewer::cmdToggleContinuousMode }, @@ -412,6 +452,7 @@ XtVaSetValues(prevPageBtn, XmNsensitive, False, NULL); XtVaSetValues(nextTenPageBtn, XmNsensitive, False, NULL); XtVaSetValues(nextPageBtn, XmNsensitive, False, NULL); + XtVaSetValues(drawBtn, XmNsensitive, False, NULL); // remove the old outline #ifndef DISABLE_OUTLINE @@ -488,6 +529,7 @@ double xu, yu, selULX, selULY, selLRX, selLRY; if (core->getHyperlinksEnabled() && + !core->getDrawEnabled() && core->cvtWindowToUser(wx, wy, &pg, &xu, &yu) && !(onlyIfNoSelection && core->getSelection(&selPg, &selULX, &selULY, &selLRX, &selLRY))) { @@ -793,9 +835,30 @@ #endif } +GBool XPDFViewer::save_helper() { + GBool flag = gFalse; + + if (core->getTemporary()) { + GString *msg = new GString("The file has been modified:\nwould you like to save changes?"); + flag = core->doQuestionDialog("Xpdf", msg); + delete msg; + } + if (flag) { + mapSaveAsDialog(); + } else { + if (core->getTemporary() && core->getDoc() != NULL) + remove(core->getDoc()->getFileName()->getCString()); + // we close the document anyways XXX not always + } + core->setTemporary(gFalse); + return flag; +} + void XPDFViewer::cmdCloseWindow(GString *args[], int nArgs, XEvent *event) { - app->close(this, gFalse); + if (!save_helper()) { + app->close(this, gFalse); + } } void XPDFViewer::cmdContinuousMode(GString *args[], int nArgs, @@ -811,6 +874,16 @@ XtVaSetValues(btn, XmNset, XmSET, NULL); } +void XPDFViewer::cmdDeleteAnnotation(GString *args[], int nArgs, + XEvent *event) { + core->deleteAnnotation(); +} + +void XPDFViewer::cmdEndDraw(GString *args[], int nArgs, + XEvent *event) { + core->endDraw(mouseX(event), mouseY(event)); +} + void XPDFViewer::cmdEndPan(GString *args[], int nArgs, XEvent *event) { core->endPan(mouseX(event), mouseY(event)); @@ -953,17 +1026,23 @@ void XPDFViewer::cmdOpen(GString *args[], int nArgs, XEvent *event) { - mapOpenDialog(gFalse); + if (!save_helper()) { + mapOpenDialog(gFalse); + } } void XPDFViewer::cmdOpenFile(GString *args[], int nArgs, XEvent *event) { - open(args[0], 1, NULL); + if (!save_helper()) { + open(args[0], 1, NULL); + } } void XPDFViewer::cmdOpenFileAtDest(GString *args[], int nArgs, XEvent *event) { - open(args[0], 1, args[1]); + if (!save_helper()) { + open(args[0], 1, args[1]); + } } void XPDFViewer::cmdOpenFileAtDestInNewWin(GString *args[], int nArgs, @@ -973,7 +1052,9 @@ void XPDFViewer::cmdOpenFileAtPage(GString *args[], int nArgs, XEvent *event) { - open(args[0], atoi(args[1]->getCString()), NULL); + if (!save_helper()) { + open(args[0], atoi(args[1]->getCString()), NULL); + } } void XPDFViewer::cmdOpenFileAtPageInNewWin(GString *args[], int nArgs, @@ -1050,7 +1131,9 @@ void XPDFViewer::cmdQuit(GString *args[], int nArgs, XEvent *event) { - app->quit(); + if (!save_helper()) { + app->quit(); + } } void XPDFViewer::cmdRaise(GString *args[], int nArgs, @@ -1267,6 +1350,11 @@ XtVaSetValues(btn, XmNset, XmUNSET, NULL); } +void XPDFViewer::cmdStartDraw(GString *args[], int nArgs, + XEvent *event) { + core->startDraw(mouseX(event), mouseY(event)); +} + void XPDFViewer::cmdStartPan(GString *args[], int nArgs, XEvent *event) { core->startPan(mouseX(event), mouseY(event)); @@ -1531,6 +1619,8 @@ NULL); XtVaSetValues(printBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); + XtVaSetValues(colorWidget, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, + NULL); XtVaSetValues(aboutBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(quitBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, @@ -1560,6 +1650,31 @@ } } +Widget XPDFViewer::mk_button(char *label, char *tooltip, Widget left, + XmString pixmap_s, void (callback)(Widget , XtPointer , XtPointer )) { + int n = 0; + Arg args[20]; + Widget ret; + + if (left) { + XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; + XtSetArg(args[n], XmNleftWidget, left); ++n; + } else { + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; + } + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNmarginWidth, 6); ++n; + XtSetArg(args[n], XmNsensitive, False); ++n; + XtSetArg(args[n], XmNlabelString, pixmap_s); ++n; + ret = XmCreatePushButton(toolBar, label, args, n); + addToolTip(ret, tooltip); + XtManageChild(ret); + XtAddCallback(ret, XmNactivateCallback, + callback, (XtPointer)this); + return ret; +} + void XPDFViewer::initToolbar(Widget parent) { Widget label, lastBtn; #ifndef USE_COMBO_BOX @@ -1579,6 +1694,14 @@ // pixmaps later emptyString = XmStringCreateLocalized(""); +#if 1 + backBtn = mk_button("back", "Back", NULL, emptyString, &backCbk); + prevTenPageBtn = mk_button("prevTenPage", "-10 pages", backBtn, emptyString, &prevTenPageCbk); + prevPageBtn = mk_button("prevPage", "Previous page", prevTenPageBtn, emptyString, &prevPageCbk); + nextPageBtn = mk_button("nextPage", "Next page", prevPageBtn, emptyString, &nextPageCbk); + nextTenPageBtn = mk_button("nextTenPage", "+10 pages", nextPageBtn, emptyString, &nextTenPageCbk); + forwardBtn = mk_button("forward", "Forward", nextTenPageBtn, emptyString, &forwardCbk); +#else // page movement buttons n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; @@ -1657,6 +1780,7 @@ XtManageChild(forwardBtn); XtAddCallback(forwardBtn, XmNactivateCallback, &forwardCbk, (XtPointer)this); +#endif // page number display n = 0; @@ -1756,6 +1880,14 @@ zoomWidget = zoomMenu; #endif +#if 1 + findBtn = mk_button("find", "Find", zoomWidget, emptyString, &findCbk); + printBtn = mk_button("print", "Print", findBtn, emptyString, &printCbk); + aboutBtn = mk_button("about", "About / help", printBtn, emptyString, &aboutCbk); + XtVaSetValues(findBtn, XmNsensitive, True, NULL); + XtVaSetValues(printBtn, XmNsensitive, True, NULL); + XtVaSetValues(aboutBtn, XmNsensitive, True, NULL); +#else // find/print/about buttons n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; @@ -1793,7 +1922,124 @@ XtManageChild(aboutBtn); XtAddCallback(aboutBtn, XmNactivateCallback, &aboutCbk, (XtPointer)this); - lastBtn = aboutBtn; +#endif + + //thickness menu + XmString st3[nThicknessMenuItems]; + for (i = 0; i < nThicknessMenuItems ; ++i) { + st3[i] = XmStringCreateLocalized(thicknessMenuInfo[i].label); + } +#if USE_COMBO_BOX + n = 0; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; + XtSetArg(args[n], XmNleftWidget, aboutBtn); ++n; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNmarginWidth, 0); ++n; + XtSetArg(args[n], XmNmarginHeight, 0); ++n; + XtSetArg(args[n], XmNcomboBoxType, XmDROP_DOWN_LIST); ++n; + XtSetArg(args[n], XmNpositionMode, XmONE_BASED); ++n; + XtSetArg(args[n], XmNcolumns, 3); ++n; + XtSetArg(args[n], XmNitems, st3); ++n; + XtSetArg(args[n], XmNitemCount, nThicknessMenuItems); ++n; + //XtSetArg(args[n], XmNvisibleItemCount, nThicknessMenuItems); ++n; + thicknessWidget = XmCreateComboBox(toolBar, "thicknessComboBox", args, n); + addToolTip(thicknessWidget, "Annotation Thickness"); + XtAddCallback(thicknessWidget, XmNselectionCallback, + &thicknessComboBoxCbk, (XtPointer)this); +#else + Widget menuPane2; + n = 0; + menuPane2 = XmCreatePulldownMenu(toolBar, "thicknessMenuPane", args, n); + for (i = 0; i < nThicknessMenuItems; ++i) { + n = 0; + XtSetArg(args[n], XmNlabelString, st3[i]); ++n; + XtSetArg(args[n], XmNuserData, (XtPointer)i); ++n; + sprintf(buf, "thickness%d", i); + btn = XmCreatePushButton(menuPane2, buf, args, n); + XtManageChild(btn); + XtAddCallback(btn, XmNactivateCallback, + &thicknessMenuCbk, (XtPointer)this); + thicknessMenuBtns[i] = btn; + } + n = 0; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; + XtSetArg(args[n], XmNleftWidget, aboutBtn); ++n; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNmarginWidth, 0); ++n; + XtSetArg(args[n], XmNmarginHeight, 0); ++n; + XtSetArg(args[n], XmNselectedPosition, 5); ++n; + XtSetArg(args[n], XmNsubMenuId, menuPane2); ++n; + thicknessWidget = XmCreateOptionMenu(toolBar, "thicknessMenu", args, n); + addToolTip(thicknessWidget, "Thickness"); +#endif + XtManageChild(thicknessWidget); + for (i = 0; i < nThicknessMenuItems; ++i) { + XmStringFree(st3[i]); + } + + //color menu + XmString st2[nColorMenuItems]; + for (i = 0; i < nColorMenuItems ; ++i) { + st2[i] = XmStringCreateLocalized(colorMenuInfo[i].label); + } +#if USE_COMBO_BOX + n = 0; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; + XtSetArg(args[n], XmNleftWidget, thicknessWidget); ++n; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNmarginWidth, 0); ++n; + XtSetArg(args[n], XmNmarginHeight, 0); ++n; + XtSetArg(args[n], XmNcomboBoxType, XmDROP_DOWN_LIST); ++n; + XtSetArg(args[n], XmNpositionMode, XmONE_BASED); ++n; + XtSetArg(args[n], XmNselectedPosition, 5); ++n; + XtSetArg(args[n], XmNcolumns, 7); ++n; + XtSetArg(args[n], XmNitems, st2); ++n; + XtSetArg(args[n], XmNitemCount, nColorMenuItems); ++n; + XtSetArg(args[n], XmNvisibleItemCount, nColorMenuItems); ++n; + colorWidget = XmCreateComboBox(toolBar, "colorComboBox", args, n); + addToolTip(colorWidget, "Annotation Color"); + XtAddCallback(colorWidget, XmNselectionCallback, + &color_Cbk, (XtPointer)this); +#else + Widget menuPane1; + n = 0; + menuPane1 = XmCreatePulldownMenu(toolBar, "colorMenuPane", args, n); + for (i = 0; i < nColorMenuItems; ++i) { + n = 0; + XtSetArg(args[n], XmNlabelString, st2[i]); ++n; + XtSetArg(args[n], XmNuserData, (XtPointer)i); ++n; + sprintf(buf, "color%d", i); + btn = XmCreatePushButton(menuPane1, buf, args, n); + XtManageChild(btn); + XtAddCallback(btn, XmNactivateCallback, + &color_Cbk, (XtPointer)this); + colorMenuBtns[i] = btn; + } + n = 0; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; + XtSetArg(args[n], XmNleftWidget, aboutBtn); ++n; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; + XtSetArg(args[n], XmNmarginWidth, 0); ++n; + XtSetArg(args[n], XmNmarginHeight, 0); ++n; + XtSetArg(args[n], XmNselectedPosition, 5); ++n; + XtSetArg(args[n], XmNsubMenuId, menuPane1); ++n; + colorWidget = XmCreateOptionMenu(toolBar, "colorMenu", args, n); + addToolTip(colorWidget, "Color"); +#endif + XtManageChild(colorWidget); + for (i = 0; i < nColorMenuItems; ++i) { + XmStringFree(st2[i]); + } + + // draw button + s = XmStringCreateLocalized("Draw"); + drawBtn = mk_button("draw", "Draw", colorWidget, s, &drawCbk); + XmStringFree(s); + lastBtn = drawBtn; // quit button n = 0; @@ -1983,6 +2229,16 @@ XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &zoomToSelectionCbk, (XtPointer)this); + + + s = XmStringCreateLocalized("Draw"); + XtSetArg(args[n], XmNlabelString, s); ++n; + btn = XmCreatePushButton(popupMenu, "draw", args, n); + XmStringFree(s); + XtManageChild(btn); + XtAddCallback(btn, XmNactivateCallback, + &drawCbk, (XtPointer)this); + n = 0; btn = XmCreateSeparator(popupMenu, "sep2", args, n); XtManageChild(btn); @@ -2348,7 +2604,6 @@ } #if USE_COMBO_BOX - void XPDFViewer::zoomComboBoxCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; @@ -2384,6 +2639,14 @@ viewer->core->takeFocus(); } +void XPDFViewer::thicknessComboBoxCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFViewer *viewer = (XPDFViewer *)ptr; + XmComboBoxCallbackStruct *data = (XmComboBoxCallbackStruct *)callData; + + viewer->core->setThickness(thicknessMenuInfo[data->item_position - 1].value); +} + #else // USE_COMBO_BOX void XPDFViewer::zoomMenuCbk(Widget widget, XtPointer ptr, @@ -2404,8 +2667,40 @@ viewer->core->takeFocus(); } +void XPDFViewer::thicknessMenuCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFViewer *viewer = (XPDFViewer *)ptr; + XmPushButtonCallbackStruct *data = (XmPushButtonCallbackStruct *)callData; + XtPointer userData; + + XtVaGetValues(widget, XmNuserData, &userData, NULL); + viewer->core->setColor(thicknessMenuInfo[(long)userData].value); +} + #endif // USE_COMBO_BOX +/* menu or combobox callback for colors */ +void XPDFViewer::color_Cbk(Widget widget, XtPointer ptr, + XtPointer callData) { + XPDFViewer *viewer = (XPDFViewer *)ptr; + long i; +#if USE_COMBO_BOX + XmComboBoxCallbackStruct *data = (XmComboBoxCallbackStruct *)callData; + i = data->item_position -1; +#else + XmPushButtonCallbackStruct *data = (XmPushButtonCallbackStruct *)callData; + XtPointer userData; + + XtVaGetValues(widget, XmNuserData, &userData, NULL); + i = (long)userData; +#endif + viewer->core->setColor( + colorMenuInfo[i].R, + colorMenuInfo[i].G, + colorMenuInfo[i].B + ); +} + void XPDFViewer::findCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; @@ -2433,18 +2728,52 @@ XtManageChild(viewer->aboutDialog); } +void XPDFViewer::drawCbk(Widget widget, XtPointer ptr, + XtPointer callData) { + Arg args[1]; + int n = 0; + XmString label_str; + + XPDFViewer *viewer = (XPDFViewer *)ptr; + if (!viewer->core->getDrawEnabled()) { + label_str = XmStringCreateLocalized("End Draw"); + if (!viewer->core->getFullScreen()) { + // disable color, and thickness drop down menu + XtVaSetValues(viewer->colorWidget, XmNsensitive, False, NULL); + XtVaSetValues(viewer->thicknessWidget, XmNsensitive, False, NULL); + } + viewer->core->enableDraw(gTrue); + } else { + if (!viewer->core->getFullScreen()) { + // enable color, and thickness drop down menu + XtVaSetValues(viewer->colorWidget, XmNsensitive, True, NULL); + XtVaSetValues(viewer->thicknessWidget, XmNsensitive, True, NULL); + } + if (viewer->core->getModified()) { + viewer->core->finalizeDraw(); + } + label_str = XmStringCreateLocalized("Draw"); + viewer->core->enableDraw(gFalse); + } + XtSetArg(args[n],XmNlabelString, label_str); ++n; + XtSetValues(widget, args, n); + XmStringFree(label_str); +} + void XPDFViewer::quitCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; - - viewer->app->quit(); + if (!viewer->save_helper()) { + viewer->app->quit(); + } } void XPDFViewer::openCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; - - viewer->mapOpenDialog(gFalse); + if (!viewer->save_helper()) { + viewer->mapOpenDialog(gFalse); + } } void XPDFViewer::openInNewWindowCbk(Widget widget, XtPointer ptr, @@ -2529,8 +2858,9 @@ void XPDFViewer::closeCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; - - viewer->app->close(viewer, gFalse); + if (!viewer->save_helper()) { + viewer->app->close(viewer, gFalse); + } } void XPDFViewer::closeMsgCbk(Widget widget, XtPointer ptr, @@ -2626,6 +2956,12 @@ XtVaSetValues(viewer->linkLabel, XmNlabelString, s, NULL); XmStringFree(s); } + + if (globalParams->getPSFile() == NULL && // NOT a Postscript file + viewer->core->getDoc() != NULL && // a PDF doc is open + !viewer->core->getDoc()->isEncrypted()) { // the document is not encrypted + XtVaSetValues(viewer->drawBtn, XmNsensitive, gTrue, NULL); + } } } @@ -2913,6 +3249,9 @@ Boolean sep; GString *fileNameStr; + if (!viewer->openInNewWindow) { + viewer->core->setModified(gFalse); + } XmStringInitContext(&context, data->value); if (XmStringGetNextSegment(context, &fileName, &charSet, &dir, &sep)) { fileNameStr = new GString(fileName); @@ -3143,7 +3482,7 @@ XmFileSelectionBoxCallbackStruct *data = (XmFileSelectionBoxCallbackStruct *)callData; char *fileName; - GString *fileNameStr; + GString *fileNameStr, *temp; XmStringContext context; XmStringCharSet charSet; XmStringDirection dir; @@ -3153,6 +3492,16 @@ if (XmStringGetNextSegment(context, &fileName, &charSet, &dir, &sep)) { fileNameStr = new GString(fileName); viewer->core->getDoc()->saveAs(fileNameStr); + temp = viewer->core->getDoc()->getFileName()->copy(); + makePathAbsolute(temp); + if (strcmp(temp->getCString(), fileNameStr->getCString()) == 0) { + // if the old name and the new one are equal, it's + // better to reset the temporary flag; if the user try + // to save the file using the name of the temporary file, + // on "quit event" xpdf could delete the file!!! + viewer->core->setTemporary(gFalse); + } + delete temp; delete fileNameStr; XtFree(charSet); XtFree(fileName); diff -ur ./xpdf/XPDFViewer.h ../../work/xpdf-3.02/xpdf/XPDFViewer.h --- ./xpdf/XPDFViewer.h 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/XPDFViewer.h 2008-07-15 16:32:51.000000000 +0200 @@ -104,6 +104,8 @@ void cmdCloseOutline(GString *args[], int nArgs, XEvent *event); void cmdCloseWindow(GString *args[], int nArgs, XEvent *event); void cmdContinuousMode(GString *args[], int nArgs, XEvent *event); + void cmdDeleteAnnotation(GString *args[], int nArgs, XEvent *event); + void cmdEndDraw(GString *args[], int nArgs, XEvent *event); void cmdEndPan(GString *args[], int nArgs, XEvent *event); void cmdEndSelection(GString *args[], int nArgs, XEvent *event); void cmdFind(GString *args[], int nArgs, XEvent *event); @@ -159,6 +161,7 @@ void cmdScrollUp(GString *args[], int nArgs, XEvent *event); void cmdScrollUpPrevPage(GString *args[], int nArgs, XEvent *event); void cmdSinglePageMode(GString *args[], int nArgs, XEvent *event); + void cmdStartDraw(GString *args[], int nArgs, XEvent *event); void cmdStartPan(GString *args[], int nArgs, XEvent *event); void cmdStartSelection(GString *args[], int nArgs, XEvent *event); void cmdToggleContinuousMode(GString *args[], int nArgs, XEvent *event); @@ -175,6 +178,10 @@ //----- GUI code: main window void initWindow(GBool fullScreen); void initToolbar(Widget parent); + Widget mk_button(char *label, char *tooltip, Widget left, XmString pixmap_s, + void (callback)(Widget , XtPointer , XtPointer )); + + #ifndef DISABLE_OUTLINE void initPanedWin(Widget parent); #endif @@ -201,16 +208,24 @@ #if USE_COMBO_BOX static void zoomComboBoxCbk(Widget widget, XtPointer ptr, XtPointer callData); + static void thicknessComboBoxCbk(Widget widget, XtPointer ptr, + XtPointer callData); #else static void zoomMenuCbk(Widget widget, XtPointer ptr, XtPointer callData); + static void thicknessMenuCbk(Widget widget, XtPointer ptr, + XtPointer callData); #endif + static void color_Cbk(Widget widget, XtPointer ptr, + XtPointer callData); static void findCbk(Widget widget, XtPointer ptr, XtPointer callData); static void printCbk(Widget widget, XtPointer ptr, XtPointer callData); static void aboutCbk(Widget widget, XtPointer ptr, XtPointer callData); + static void drawCbk(Widget widget, XtPointer ptr, + XtPointer callData); static void quitCbk(Widget widget, XtPointer ptr, XtPointer callData); static void openCbk(Widget widget, XtPointer ptr, @@ -248,6 +263,7 @@ XtPointer callData); #endif + GBool save_helper(); // save temp file on close //----- GUI code: "about" dialog void initAboutDialog(); @@ -289,6 +305,7 @@ XPDFApp *app; GBool ok; + GBool modified; Display *display; int screenNum; @@ -318,11 +335,15 @@ #else Widget zoomMenu; Widget zoomMenuBtns[nZoomMenuItems]; + Widget colorMenuBtns[nColorMenuItems]; #endif Widget zoomWidget; Widget findBtn; Widget printBtn; Widget aboutBtn; + Widget thicknessWidget; + Widget colorWidget; + Widget drawBtn; Widget linkLabel; Widget quitBtn; Widget popupMenu; diff -ur ./xpdf/XRef.cc ../../work/xpdf-3.02/xpdf/XRef.cc --- ./xpdf/XRef.cc 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/XRef.cc 2008-07-15 16:32:51.000000000 +0200 @@ -197,6 +197,7 @@ ok = gTrue; errCode = errNone; size = 0; + numObjects = 0; entries = NULL; streamEnds = NULL; streamEndsLen = 0; @@ -367,6 +368,7 @@ goto err1; } n = obj.getInt(); + numObjects += n; obj.free(); if (first < 0 || n < 0 || first + n < 0) { goto err1; @@ -490,6 +492,7 @@ entries[i].type = xrefEntryFree; } size = newSize; + numObjects += newSize - size; // XXX this is a duplicate } if (!dict->lookupNF("W", &obj)->isArray() || @@ -580,6 +583,7 @@ entries[i].type = xrefEntryFree; } size = newSize; + numObjects += n; } for (i = first; i < first + n; ++i) { if (w[0] == 0) { @@ -645,6 +649,7 @@ gfree(entries); size = 0; + numObjects = 0; entries = NULL; error(-1, "PDF file is damaged - attempting to reconstruct xref table..."); @@ -743,6 +748,7 @@ } } + numObjects = num; if (gotRoot) return gTrue; diff -ur ./xpdf/XRef.h ../../work/xpdf-3.02/xpdf/XRef.h --- ./xpdf/XRef.h 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/XRef.h 2008-07-15 16:32:51.000000000 +0200 @@ -79,7 +79,7 @@ Object *getDocInfoNF(Object *obj); // Return the number of objects in the xref table. - int getNumObjects() { return size; } + int getNumObjects() { return numObjects; } // XXX perhaps useless ? // Return the offset of the last xref table. Guint getLastXRefPos() { return lastXRefPos; } @@ -104,6 +104,7 @@ // at beginning of file) XRefEntry *entries; // xref entries int size; // size of array + int numObjects; //number of objects inside entries array XXX perhaps useless ? int rootNum, rootGen; // catalog dict GBool ok; // true if xref table is valid int errCode; // error code (if is false) diff -ur ./xpdf/about-text.h ../../work/xpdf-3.02/xpdf/about-text.h --- ./xpdf/about-text.h 2007-02-27 23:05:52.000000000 +0100 +++ ../../work/xpdf-3.02/xpdf/about-text.h 2008-07-15 16:32:51.000000000 +0200 @@ -19,7 +19,7 @@ "are copyright 1985-2006 Adobe Systems Inc.", " ", "Mouse bindings:", - " button 1: select text / follow link", + " button 1: select text / follow link / select annotations / draw annotations", " button 2: pan window", " button 3: menu", " ", @@ -32,7 +32,7 @@ " n = next page", " p = previous page", " = = scroll down", - " = = = scroll up", + " = = scroll up", " v = forward (history path)", " b = backward (history path)", " 0 / + / - = zoom zero / in / out", @@ -42,6 +42,7 @@ " q = quit", " / = top / bottom of page", " = scroll", + " = delete selected annotation", " ", "For more information, please read the xpdf(1) man page.", NULL