/* * Copyright (c) 1987, 1988, 1989 Stanford University * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Stanford not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Stanford makes no representations about * the suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // implements class PictSelection. #include "ipaint.h" #include "istring.h" #include "listifont.h" #include "slellipses.h" #include "sllines.h" #include "slpict.h" #include "slpolygons.h" #include "slsplines.h" #include "sltext.h" #include #include // PictSelection initializes its graphic state. PictSelection::PictSelection (Graphic* gs) : Selection(gs) { valid = true; } // PictSelection knows it's the outermost PictSelection because it was // called with a FILE* pointer, so it must read a version number, skip // over its name, read its graphic state and children, and scale // itself back to screen coordinates when it's finished. PictSelection::PictSelection (FILE* stream, State* state) : Selection(nil) { int fd = fileno(stream); filebuf fbuf(fd); istream from(&fbuf); // istream from(fd); ReadVersion(from); ReadGridSpacing(from, state); if (versionnumber < NONREDUNDANTVERSION) { Skip(from); } ReadPictGS(from, state); ReadChildren(from, state); ScaleToScreenCoords(); Transformer* t = GetTransformer(); if (versionnumber < NONROTATEDVERSION && t != nil && t->Rotated90()) { t = identity; Translate(0.0, -8.5*inches); Rotate(90.0, 0.0, 0.0); Picture::Propagate(); } valid = from.good(); } // Copy returns a copy of the PictSelection. Graphic* PictSelection::Copy () { Selection* copy = new PictSelection(this); for (First(); !AtEnd(); Next()) { copy->Append(GetCurrent()->Copy()); } return copy; } // HasChildren returns true so Idraw can ungroup this Picture. bool PictSelection::HasChildren () { return Picture::HasChildren(); } // Propagate must preserve the PictSelection's transformation matrix // if it has any. void PictSelection::Propagate () { Transformer* original = GetTransformer(); if (original != nil) { original->Reference(); Picture::Propagate(); SetTransformer(original); Unref(original); } else { Picture::Propagate(); } } // WritePicture writes the picture and returns true if the write // succeeded or false if some IO error occurred. It omits the // Postscript prologue and trailer if called with verbose false to // speed up cutting and pasting pictures between drawings. bool PictSelection::WritePicture ( FILE* stream, State* state, bool verbose ) { #if !defined(__GNUG__) // incompatible with g++, but works around a cfront bug filebuf fb(stream); ostream to(&fb); #else int fd = fileno(stream); filebuf fbuf(fd); ostream to(&fbuf); // ostream to(fd); #endif WritePicture(to, state, verbose); return to.good(); } // PictSelection knows it's not the outermost PictSelection so it only // reads data to initialize its graphic state and create its children // Selections. PictSelection::PictSelection (istream& from, State* state) : Selection(nil) { ReadPictGS(from, state); ReadChildren(from, state); valid = from.good(); } // ReadChildren loops determining which kind of Selection follows and // creating it until it reads "end" which means all of the children // have been created. void PictSelection::ReadChildren (istream& from, State* state) { while (from.good()) { Skip(from); Selection* child = nil; from >> buf; if (strcmp(buf, "BSpl") == 0) { child = new BSplineSelection(from, state); } else if (strcmp(buf, "Circ") == 0) { child = new CircleSelection(from, state); } else if (strcmp(buf, "CBSpl") == 0) { child = new ClosedBSplineSelection(from, state); } else if (strcmp(buf, "Elli") == 0) { child = new EllipseSelection(from, state); } else if (strcmp(buf, "Line") == 0) { child = new LineSelection(from, state); } else if (strcmp(buf, "MLine") == 0) { child = new MultiLineSelection(from, state); } else if (strcmp(buf, "Pict") == 0) { child = new PictSelection(from, state); } else if (strcmp(buf, "Poly") == 0) { child = new PolygonSelection(from, state); } else if (strcmp(buf, "Rect") == 0) { child = new RectSelection(from, state); } else if (strcmp(buf, "Text") == 0) { child = new TextSelection(from, state); } else if (strcmp(buf, "eop") == 0) { break; } else { fprintf(stderr, "unknown Selection %s, skipping\n", buf); continue; } if (from.good()) { Append(child); } else { delete child; } } } // WritePicture writes the picture's data and Postscript code to print // it wrapped in Postscript comments that minimally conform to version // 1.0 of Adobe Systems's structuring conventions for Postscript. The // picture must remove itself from its parent if it has a parent to // prevent the parent's transformation from affecting the picture's // calculation of its bounding box. void PictSelection::WritePicture (ostream& to, State* state, bool verbose) { Picture* parent = (Picture*) Parent(); if (parent != nil) { parent->SetCurrent(this); parent->Remove(this); } ScaleToPostscriptCoords(); if (verbose) { WriteComments(to); WritePrologue(to); WriteVersion(to); WriteGridSpacing(to, state); WriteDrawing(to); WriteTrailer(to); } else { WriteVersion(to); WriteDrawing(to); } ScaleToScreenCoords(); if (parent != nil) { parent->InsertBeforeCur(this); } } // WriteComments writes information about the picture such as the // fonts used in it and the smallest bounding box enclosing it. void PictSelection::WriteComments (ostream& to) { to << "%!PS-Adobe-2.0 EPSF-1.2\n"; to << "%%DocumentFonts:"; int linelen = strlen("%%DocumentFonts:"); const int MAXLINELEN = 256; IFontList* fontlist = new IFontList; CollectFonts(fontlist); for (fontlist->First(); !fontlist->AtEnd(); fontlist->Next()) { IFont* font = fontlist->GetCur()->GetFont(); if (linelen + strlen(font->GetPrintFont()) + 2 <= MAXLINELEN) { to << " "; ++linelen; } else { to << "\n%%+ "; linelen = strlen("%%+ "); } to << font->GetPrintFont(); linelen += strlen(font->GetPrintFont()); } to << "\n"; delete fontlist; to << "%%Pages: 1\n"; Coord l, b, r, t; GetBox(l, b, r, t); to << "%%BoundingBox: " << l << " " << b << " " << r << " " << t << "\n"; to << "%%EndComments\n\n"; to << "50 dict begin\n\n"; } // WritePrologue writes definitions of Postscript procedures to draw // Selections. You should not rename or delete the "exported" // capitalized procedures because old drawings rely on them, but you // can rename or delete the "internal" uncapitalized procedures. void PictSelection::WritePrologue (ostream& to) { to << "/arrowHeight " << ARROWHEIGHT << " def\n"; to << "/arrowWidth " << ARROWWIDTH << " def\n"; to << "/none null def\n"; to << "/numGraphicParameters 17 def\n"; to << "/stringLimit 65535 def\n\n"; to << "/Begin {\n"; to << "save\n"; to << "numGraphicParameters dict begin\n"; to << "} def\n\n"; to << "/End {\n"; to << "end\n"; to << "restore\n"; to << "} def\n\n"; to << "/SetB {\n"; to << "dup type /nulltype eq {\n"; to << "pop\n"; to << "false /brushRightArrow idef\n"; to << "false /brushLeftArrow idef\n"; to << "true /brushNone idef\n"; to << "} {\n"; to << "/brushDashOffset idef\n"; to << "/brushDashArray idef\n"; to << "0 ne /brushRightArrow idef\n"; to << "0 ne /brushLeftArrow idef\n"; to << "/brushWidth idef\n"; to << "false /brushNone idef\n"; to << "} ifelse\n"; to << "} def\n\n"; to << "/SetCFg {\n"; to << "/fgblue idef\n"; to << "/fggreen idef\n"; to << "/fgred idef\n"; to << "} def\n\n"; to << "/SetCBg {\n"; to << "/bgblue idef\n"; to << "/bggreen idef\n"; to << "/bgred idef\n"; to << "} def\n\n"; to << "/SetF {\n"; to << "/printSize idef\n"; to << "/printFont idef\n"; to << "} def\n\n"; to << "/SetP {\n"; to << "dup type /nulltype eq {\n"; to << "pop true /patternNone idef\n"; to << "} {\n"; to << "/patternGrayLevel idef\n"; to << "patternGrayLevel -1 eq {\n"; to << "/patternString idef\n"; to << "} if\n"; to << "false /patternNone idef\n"; to << "} ifelse\n"; to << "} def\n\n"; to << "/BSpl {\n"; to << "0 begin\n"; to << "storexyn\n"; to << "newpath\n"; to << "n 1 gt {\n"; to << "0 0 0 0 0 0 1 1 true subspline\n"; to << "n 2 gt {\n"; to << "0 0 0 0 1 1 2 2 false subspline\n"; to << "1 1 n 3 sub {\n"; to << "/i exch def\n"; to << "i 1 sub dup i dup i 1 add dup i 2 add dup false subspline\n"; to << "} for\n"; to << "n 3 sub dup n 2 sub dup n 1 sub dup 2 copy false subspline\n"; to << "} if\n"; to << "n 2 sub dup n 1 sub dup 2 copy 2 copy false subspline\n"; to << "patternNone not brushLeftArrow not brushRightArrow not and and { "; to << "ifill } if\n"; to << "brushNone not { istroke } if\n"; to << "0 0 1 1 leftarrow\n"; to << "n 2 sub dup n 1 sub dup rightarrow\n"; to << "} if\n"; to << "end\n"; to << "} dup 0 4 dict put def\n\n"; to << "/Circ {\n"; to << "newpath\n"; to << "0 360 arc\n"; to << "patternNone not { ifill } if\n"; to << "brushNone not { istroke } if\n"; to << "} def\n\n"; to << "/CBSpl {\n"; to << "0 begin\n"; to << "dup 2 gt {\n"; to << "storexyn\n"; to << "newpath\n"; to << "n 1 sub dup 0 0 1 1 2 2 true subspline\n"; to << "1 1 n 3 sub {\n"; to << "/i exch def\n"; to << "i 1 sub dup i dup i 1 add dup i 2 add dup false subspline\n"; to << "} for\n"; to << "n 3 sub dup n 2 sub dup n 1 sub dup 0 0 false subspline\n"; to << "n 2 sub dup n 1 sub dup 0 0 1 1 false subspline\n"; to << "patternNone not { ifill } if\n"; to << "brushNone not { istroke } if\n"; to << "} {\n"; to << "Poly\n"; to << "} ifelse\n"; to << "end\n"; to << "} dup 0 4 dict put def\n\n"; to << "/Elli {\n"; to << "0 begin\n"; to << "newpath\n"; to << "4 2 roll\n"; to << "translate\n"; to << "scale\n"; to << "0 0 1 0 360 arc\n"; to << "patternNone not { ifill } if\n"; to << "brushNone not { istroke } if\n"; to << "end\n"; to << "} dup 0 1 dict put def\n\n"; to << "/Line {\n"; to << "0 begin\n"; to << "2 storexyn\n"; to << "newpath\n"; to << "x 0 get y 0 get moveto\n"; to << "x 1 get y 1 get lineto\n"; to << "brushNone not { istroke } if\n"; to << "0 0 1 1 leftarrow\n"; to << "0 0 1 1 rightarrow\n"; to << "end\n"; to << "} dup 0 4 dict put def\n\n"; to << "/MLine {\n"; to << "0 begin\n"; to << "storexyn\n"; to << "newpath\n"; to << "n 1 gt {\n"; to << "x 0 get y 0 get moveto\n"; to << "1 1 n 1 sub {\n"; to << "/i exch def\n"; to << "x i get y i get lineto\n"; to << "} for\n"; to << "patternNone not brushLeftArrow not brushRightArrow not and and { "; to << "ifill } if\n"; to << "brushNone not { istroke } if\n"; to << "0 0 1 1 leftarrow\n"; to << "n 2 sub dup n 1 sub dup rightarrow\n"; to << "} if\n"; to << "end\n"; to << "} dup 0 4 dict put def\n\n"; to << "/Poly {\n"; to << "3 1 roll\n"; to << "newpath\n"; to << "moveto\n"; to << "-1 add\n"; to << "{ lineto } repeat\n"; to << "closepath\n"; to << "patternNone not { ifill } if\n"; to << "brushNone not { istroke } if\n"; to << "} def\n\n"; to << "/Rect {\n"; to << "0 begin\n"; to << "/t exch def\n"; to << "/r exch def\n"; to << "/b exch def\n"; to << "/l exch def\n"; to << "newpath\n"; to << "l b moveto\n"; to << "l t lineto\n"; to << "r t lineto\n"; to << "r b lineto\n"; to << "closepath\n"; to << "patternNone not { ifill } if\n"; to << "brushNone not { istroke } if\n"; to << "end\n"; to << "} dup 0 4 dict put def\n\n"; to << "/Text {\n"; to << "ishow\n"; to << "} def\n\n"; to << "/idef {\n"; to << "dup where { pop pop pop } { exch def } ifelse\n"; to << "} def\n\n"; to << "/ifill {\n"; to << "0 begin\n"; to << "gsave\n"; to << "patternGrayLevel -1 ne {\n"; to << "fgred bgred fgred sub patternGrayLevel mul add\n"; to << "fggreen bggreen fggreen sub patternGrayLevel mul add\n"; to << "fgblue bgblue fgblue sub patternGrayLevel mul add setrgbcolor\n"; to << "eofill\n"; to << "} {\n"; to << "eoclip\n"; to << "originalCTM setmatrix\n"; to << "pathbbox /t exch def /r exch def /b exch def /l exch def\n"; to << "/w r l sub ceiling cvi def\n"; to << "/h t b sub ceiling cvi def\n"; to << "/imageByteWidth w 8 div ceiling cvi def\n"; to << "/imageHeight h def\n"; to << "bgred bggreen bgblue setrgbcolor\n"; to << "eofill\n"; to << "fgred fggreen fgblue setrgbcolor\n"; to << "w 0 gt h 0 gt and {\n"; to << "l b translate w h scale\n"; to << "w h true [w 0 0 h neg 0 h] { patternproc } imagemask\n"; to << "} if\n"; to << "} ifelse\n"; to << "grestore\n"; to << "end\n"; to << "} dup 0 8 dict put def\n\n"; to << "/istroke {\n"; to << "gsave\n"; to << "brushDashOffset -1 eq {\n"; to << "[] 0 setdash\n"; to << "1 setgray\n"; to << "} {\n"; to << "brushDashArray brushDashOffset setdash\n"; to << "fgred fggreen fgblue setrgbcolor\n"; to << "} ifelse\n"; to << "brushWidth setlinewidth\n"; to << "originalCTM setmatrix\n"; to << "stroke\n"; to << "grestore\n"; to << "} def\n\n"; to << "/ishow {\n"; to << "0 begin\n"; to << "gsave\n"; to << "fgred fggreen fgblue setrgbcolor\n"; to << "/fontDict printFont findfont printSize scalefont dup setfont def\n"; to << "/descender fontDict begin 0 [FontBBox] 1 get FontMatrix end\n"; to << "transform exch pop def\n"; to << "/vertoffset 0 descender sub printSize sub printFont /Courier ne\n"; to << "printFont /Courier-Bold ne and { 1 add } if def {\n"; to << "0 vertoffset moveto show\n"; to << "/vertoffset vertoffset printSize sub def\n"; to << "} forall\n"; to << "grestore\n"; to << "end\n"; to << "} dup 0 3 dict put def\n\n"; to << "/patternproc {\n"; to << "0 begin\n"; to << "/patternByteLength patternString length def\n"; to << "/patternHeight patternByteLength 8 mul sqrt cvi def\n"; to << "/patternWidth patternHeight def\n"; to << "/patternByteWidth patternWidth 8 idiv def\n"; to << "/imageByteMaxLength imageByteWidth imageHeight mul\n"; to << "stringLimit patternByteWidth sub min def\n"; to << "/imageMaxHeight imageByteMaxLength imageByteWidth idiv "; to << "patternHeight idiv\n"; to << "patternHeight mul patternHeight max def\n"; to << "/imageHeight imageHeight imageMaxHeight sub store\n"; to << "/imageString imageByteWidth imageMaxHeight mul patternByteWidth "; to << "add string def\n"; to << "0 1 imageMaxHeight 1 sub {\n"; to << "/y exch def\n"; to << "/patternRow y patternByteWidth mul patternByteLength mod def\n"; to << "/patternRowString patternString patternRow patternByteWidth "; to << "getinterval def\n"; to << "/imageRow y imageByteWidth mul def\n"; to << "0 patternByteWidth imageByteWidth 1 sub {\n"; to << "/x exch def\n"; to << "imageString imageRow x add patternRowString putinterval\n"; to << "} for\n"; to << "} for\n"; to << "imageString\n"; to << "end\n"; to << "} dup 0 12 dict put def\n\n"; to << "/min {\n"; to << "dup 3 2 roll dup 4 3 roll lt { exch } if pop\n"; to << "} def\n\n"; to << "/max {\n"; to << "dup 3 2 roll dup 4 3 roll gt { exch } if pop\n"; to << "} def\n\n"; to << "/arrowhead {\n"; to << "0 begin\n"; to << "transform originalCTM itransform\n"; to << "/taily exch def\n"; to << "/tailx exch def\n"; to << "transform originalCTM itransform\n"; to << "/tipy exch def\n"; to << "/tipx exch def\n"; to << "/dy tipy taily sub def\n"; to << "/dx tipx tailx sub def\n"; to << "/angle dx 0 ne dy 0 ne or { dy dx atan } { 90 } ifelse def\n"; to << "gsave\n"; to << "originalCTM setmatrix\n"; to << "tipx tipy translate\n"; to << "angle rotate\n"; to << "newpath\n"; to << "0 0 moveto\n"; to << "arrowHeight neg arrowWidth 2 div lineto\n"; to << "arrowHeight neg arrowWidth 2 div neg lineto\n"; to << "closepath\n"; to << "patternNone not {\n"; to << "originalCTM setmatrix\n"; to << "/padtip arrowHeight 2 exp 0.25 arrowWidth 2 exp mul add sqrt "; to << "brushWidth mul\n"; to << "arrowWidth div def\n"; to << "/padtail brushWidth 2 div def\n"; to << "tipx tipy translate\n"; to << "angle rotate\n"; to << "padtip 0 translate\n"; to << "arrowHeight padtip add padtail add arrowHeight div dup scale\n"; to << "arrowheadpath\n"; to << "ifill\n"; to << "} if\n"; to << "brushNone not {\n"; to << "originalCTM setmatrix\n"; to << "tipx tipy translate\n"; to << "angle rotate\n"; to << "arrowheadpath\n"; to << "istroke\n"; to << "} if\n"; to << "grestore\n"; to << "end\n"; to << "} dup 0 9 dict put def\n\n"; to << "/arrowheadpath {\n"; to << "newpath\n"; to << "0 0 moveto\n"; to << "arrowHeight neg arrowWidth 2 div lineto\n"; to << "arrowHeight neg arrowWidth 2 div neg lineto\n"; to << "closepath\n"; to << "} def\n\n"; to << "/leftarrow {\n"; to << "0 begin\n"; to << "y exch get /taily exch def\n"; to << "x exch get /tailx exch def\n"; to << "y exch get /tipy exch def\n"; to << "x exch get /tipx exch def\n"; to << "brushLeftArrow { tipx tipy tailx taily arrowhead } if\n"; to << "end\n"; to << "} dup 0 4 dict put def\n\n"; to << "/rightarrow {\n"; to << "0 begin\n"; to << "y exch get /tipy exch def\n"; to << "x exch get /tipx exch def\n"; to << "y exch get /taily exch def\n"; to << "x exch get /tailx exch def\n"; to << "brushRightArrow { tipx tipy tailx taily arrowhead } if\n"; to << "end\n"; to << "} dup 0 4 dict put def\n\n"; to << "/midpoint {\n"; to << "0 begin\n"; to << "/y1 exch def\n"; to << "/x1 exch def\n"; to << "/y0 exch def\n"; to << "/x0 exch def\n"; to << "x0 x1 add 2 div\n"; to << "y0 y1 add 2 div\n"; to << "end\n"; to << "} dup 0 4 dict put def\n\n"; to << "/thirdpoint {\n"; to << "0 begin\n"; to << "/y1 exch def\n"; to << "/x1 exch def\n"; to << "/y0 exch def\n"; to << "/x0 exch def\n"; to << "x0 2 mul x1 add 3 div\n"; to << "y0 2 mul y1 add 3 div\n"; to << "end\n"; to << "} dup 0 4 dict put def\n\n"; to << "/subspline {\n"; to << "0 begin\n"; to << "/movetoNeeded exch def\n"; to << "y exch get /y3 exch def\n"; to << "x exch get /x3 exch def\n"; to << "y exch get /y2 exch def\n"; to << "x exch get /x2 exch def\n"; to << "y exch get /y1 exch def\n"; to << "x exch get /x1 exch def\n"; to << "y exch get /y0 exch def\n"; to << "x exch get /x0 exch def\n"; to << "x1 y1 x2 y2 thirdpoint\n"; to << "/p1y exch def\n"; to << "/p1x exch def\n"; to << "x2 y2 x1 y1 thirdpoint\n"; to << "/p2y exch def\n"; to << "/p2x exch def\n"; to << "x1 y1 x0 y0 thirdpoint\n"; to << "p1x p1y midpoint\n"; to << "/p0y exch def\n"; to << "/p0x exch def\n"; to << "x2 y2 x3 y3 thirdpoint\n"; to << "p2x p2y midpoint\n"; to << "/p3y exch def\n"; to << "/p3x exch def\n"; to << "movetoNeeded { p0x p0y moveto } if\n"; to << "p1x p1y p2x p2y p3x p3y curveto\n"; to << "end\n"; to << "} dup 0 17 dict put def\n\n"; to << "/storexyn {\n"; to << "/n exch def\n"; to << "/y n array def\n"; to << "/x n array def\n"; to << "n 1 sub -1 0 {\n"; to << "/i exch def\n"; to << "y i 3 2 roll put\n"; to << "x i 3 2 roll put\n"; to << "} for\n"; to << "} def\n\n"; to << "%%EndProlog\n\n"; } // WriteDrawing writes code to store the picture's transformation // matrix in a Postscript variable and code to draw the picture. void PictSelection::WriteDrawing (ostream& to) { to << "\n\n%%Page: 1 1\n\n"; to << "Begin\n"; WritePictGS(to); to << "/originalCTM matrix currentmatrix def\n\n"; for (First(); !AtEnd(); Next()) { Selection* s = GetCurrent(); s->WriteData(to); } to << "End " << startdata << " eop\n\n"; to << "showpage\n\n"; } // WriteData writes the PictSelection's data and its children // Selections' data with Postscript code to draw them. void PictSelection::WriteData (ostream& to) { to << "Begin " << startdata << " Pict\n"; WritePictGS(to); to << "\n"; for (First(); !AtEnd(); Next()) { Selection* s = GetCurrent(); s->WriteData(to); } to << "End " << startdata << " eop\n\n"; } // WriteTrailer writes clean up code. void PictSelection::WriteTrailer (ostream& to) { to << "%%Trailer\n\n"; to << "end\n"; } // ScaleToPostscriptCoords scales the picture to Postscript // coordinates if screen and Postscript inches are different. void PictSelection::ScaleToPostscriptCoords () { const double postscriptinch = 72.; if (inch != postscriptinch) { double topostscript = postscriptinch / inch; Scale(topostscript, topostscript); } } // ScaleToScreenCoords scales the picture back to screen coordinates // if screen and Postscript inches are different. void PictSelection::ScaleToScreenCoords () { const double postscriptinch = 72.; if (inch != postscriptinch) { double toscreen = inch / postscriptinch; Scale(toscreen, toscreen); } } // CollectFonts adds its font, if it has one, to the list without // checking if the PictSelection contains any TextSelections. If it // doesn't have a font, it collects its children TextSelection's // fonts. void PictSelection::CollectFonts (IFontList* fontlist) { if (GetFont() != nil) { Merge((IFont*) GetFont(), fontlist); } else { for (First(); !AtEnd(); Next()) { Selection* s = GetCurrent(); if (s->HasChildren()) { ((PictSelection*) s)->CollectFonts(fontlist); } else if (s->IsA(TEXTSELECTION)) { Merge((IFont*) s->GetFont(), fontlist); } } } } // Merge merges the print font with the list of all print fonts unless // the list already has it. void PictSelection::Merge (IFont* font, IFontList* fontlist) { bool found = false; if (font == nil) { found = true; } else if (fontlist->Find(font)) { found = true; } else { for (fontlist->First(); !fontlist->AtEnd(); fontlist->Next()) { IFont* cmp = fontlist->GetCur()->GetFont(); if (strcmp(font->GetPrintFont(), cmp->GetPrintFont()) == 0) { found = true; break; } } } if (!found) { fontlist->Append(new IFontNode(font)); } }