<> <> <> <> <<>> DIRECTORY Basics USING [BytePair, CompareCard, Comparison], CubicSplines USING [SplineType, KnotSequence, KnotSequenceRec, MakeSpline, X, Y], CubicPaths USING [Path, EnumeratePath, PathFromCubic], FS USING [ComponentPositions, ExpandName, StreamOpen], GFileFormatDefs, GriffinImageUtils, Imager USING [Context, DoSaveAll, Error, Font, MaskFillTrajectory, MaskStrokeTrajectory, micasPerPoint, Move, RotateT, SetColor, SetFont, SetStrokeWidth, SetXRel, SetXY, SetYRel, ShowRope], ImagerColor USING [ColorFromRGB, RGBFromHSV], ImagerFont USING [Find, RopeWidth, Scale], ImagerPath USING [LineTo, MoveTo, CurveTo, Trajectory, MoveToProc, CurveToProc], IO USING [GetBlock, GetChar, SetIndex, STREAM], MessageWindow USING [Append], PrincOpsUtils USING [LongCopy], RealConvert USING [Mesa5ToIeee], RedBlackTree USING [Create, Insert, Lookup, Table], Rope USING [Cat, Compare, Equal, FromProc, FromRefText, Substr]; GriffinImageUtilsImpl: CEDAR MONITOR IMPORTS Basics, CubicSplines, CubicPaths, FS, IO, Imager, ImagerColor, ImagerFont, ImagerPath, MessageWindow, PrincOpsUtils, RealConvert, RedBlackTree, Rope EXPORTS GriffinImageUtils ~ BEGIN OPEN GriffinImageUtils; SplineType: TYPE ~ CubicSplines.SplineType; ReadGriffinImage: PUBLIC ENTRY PROC [name: ROPE] RETURNS [g: GriffinImage] ~ TRUSTED { <> ENABLE UNWIND => NULL; Name: UNSAFE PROC [chars: PACKED ARRAY [0 .. GFileFormatDefs.cNameChars] OF CHARACTER] RETURNS [rope: ROPE _ NIL] ~ { GetChar: PROC RETURNS [c: CHAR] ~ CHECKED { index _ index+1; c _ chars[index]; }; len, index: CARDINAL _ 0; len _ LOOPHOLE[chars[0], CARDINAL]; rope _ Rope.FromProc[len, GetChar]; }; diskHandle: IO.STREAM; gHeader: GFileFormatDefs.GFileHeader; name _ FixFileName[name, ".griffin"]; diskHandle _ FS.StreamOpen[name]; gHeader _ ReadHeader[diskHandle]; --Set up header info g _ NEW[GriffinImageRep[gHeader.numfigs]]; --Create top level structure g.header _ NEW[HeaderRep _ [ majorVersion: gHeader.majversion, minorVersion: gHeader.minversion, creatorName: Name[gHeader.creatorname], portfolioName: Name[gHeader.portfolioname], createTime: LOOPHOLE[gHeader.createtime] ]]; FOR figure: CARDINAL IN [1..g.nFigures) DO ReadFigure[g, figure, diskHandle, gHeader]; ENDLOOP; }; ConvertRealFromMesa5: PROC [in: REAL] RETURNS [REAL] ~ { RETURN [RealConvert.Mesa5ToIeee[LOOPHOLE[in]]]; }; ConvertRealFromReal: PROC [in: REAL] RETURNS [REAL] ~ { RETURN [in] }; ConvertReal: PROC [in: REAL] RETURNS [REAL]; ReadFigure: INTERNAL PROC [g: GriffinImage, f: CARDINAL, diskHandle: IO.STREAM, gHeader: GFileFormatDefs.GFileHeader] ~ TRUSTED { <<>> <> figureName: GFileFormatDefs.GFileFigureName; hControl: GFileFormatDefs.GFileHardcopyController; dControl: GFileFormatDefs.GFileDisplayController; ReadFigureInformation: INTERNAL PROC ~ TRUSTED { MoveToSector[diskHandle, gHeader.figure[f]]; ReadStructure[diskHandle, @figureName, GFileFormatDefs.lGFileFigureName]; ReadStructure[diskHandle, @hControl, GFileFormatDefs.lGFileHardcopyController]; ReadStructure[diskHandle, @dControl, GFileFormatDefs.lGFileDisplayController]; }; ConvertDisplayController: INTERNAL PROC [d: GFileFormatDefs.GFileDisplayController] RETURNS [DisplayController] ~ TRUSTED { RETURN [[ centerX: d.centerx, centerY: d.centery, width: d.width, height: d.height, xScale: ConvertReal[d.xscale], yScale: ConvertReal[d.yscale], gridXo: ConvertReal[d.gridxo], gridYo: ConvertReal[d.gridyo], gridSize: d.gridsize, pairs: NIL ]]; }; ConvertHardcopyController: INTERNAL PROC [h: GFileFormatDefs.GFileHardcopyController] RETURNS [GFileFormatDefs.GFileHardcopyController] ~ CHECKED { RETURN [[ centerx: ConvertReal[h.centerx], centery: ConvertReal[h.centery], width: ConvertReal[h.width], height: ConvertReal[h.height], presscenterx: h.presscenterx, presscentery: h.presscentery, scale: ConvertReal[h.scale] ]]; }; Name: INTERNAL PROC [chars: PACKED ARRAY [0 .. GFileFormatDefs.figureNameChars] OF CHARACTER] RETURNS [rope: ROPE _ NIL] ~ CHECKED { GetChar: PROC RETURNS [c: CHAR] ~ CHECKED { index _ index+1; c _ chars[index]; }; len, index: CARDINAL _ 0; len _ LOOPHOLE[chars[0], CARDINAL]; rope _ Rope.FromProc[len, GetChar]; }; font: FontDir; styles: Styles; objects: Objects; g.figures[f] _ NEW[FigureRep]; ReadFigureInformation[]; font _ ReadFontDir[diskHandle]; styles _ ReadStyles[diskHandle]; objects _ ReadObjects[diskHandle]; g.figures[f]^ _ [ font: font, styles: styles, objects: objects, display: ConvertDisplayController[dControl], hardcopy: ConvertHardcopyController[hControl], name: Name[figureName] ]; }; ReadFontDir: INTERNAL UNSAFE PROC [diskHandle: IO.STREAM] RETURNS [font: FontDir] ~ UNCHECKED { nFonts: CARDINAL ~ ReadCardinal[diskHandle]; font _ NEW[FontDirRep[nFonts]]; FOR f: CARDINAL IN [0..nFonts) DO font[f] _ ReadFont[diskHandle]; ENDLOOP; }; ReadFont: INTERNAL UNSAFE PROC [diskHandle: IO.STREAM] RETURNS [f: Font] ~ UNCHECKED { Name: INTERNAL PROC [chars: PACKED ARRAY [0 .. GFileFormatDefs.fontChars] OF CHARACTER] RETURNS [rope: ROPE _ NIL] ~ CHECKED { GetChar: PROC RETURNS [c: CHAR] ~ CHECKED { index _ index+1; c _ chars[index]; }; len, index: CARDINAL _ 0; len _ LOOPHOLE[chars[0], CARDINAL]; rope _ Rope.FromProc[len, GetChar]; }; font: REF GFileFormatDefs.GFileFont _ NEW[GFileFormatDefs.GFileFont]; ReadStructure[diskHandle, LOOPHOLE[font], GFileFormatDefs.lGFileFont]; f _ [ points: font.points, face: font.face, rotation: font.rotation, name: Name[font.char] ]; }; ReadStyles: INTERNAL UNSAFE PROC [diskHandle: IO.STREAM] RETURNS [styles: Styles] ~ UNCHECKED { styleCount: CARDINAL _ ReadCardinal[diskHandle]; styles _ NEW[StylesRep[styleCount]]; FOR style: CARDINAL IN [0..styles.nStyles) DO styles[style] _ ReadStyle[diskHandle]; ENDLOOP; }; ReadStyle: INTERNAL UNSAFE PROC [diskHandle: IO.STREAM] RETURNS [style: Style] ~ UNCHECKED { MakeEnd: INTERNAL PROC [endType: CARDINAL, dx, dy, a, b, c: REAL] RETURNS [End] ~ CHECKED { <> RETURN [[ type: LOOPHOLE[endType], dx: ConvertReal[dx], dy: ConvertReal[dy], a: ConvertReal[a], b: ConvertReal[b], c: ConvertReal[c] ]] }; Name: INTERNAL PROC [chars: PACKED ARRAY [0 .. GFileFormatDefs.sNameChars] OF CHARACTER] RETURNS [rope: ROPE _ NIL] ~ CHECKED { GetChar: PROC RETURNS [c: CHAR] ~ CHECKED { index _ index+1; c _ chars[index]; }; len, index: CARDINAL _ 0; len _ LOOPHOLE[chars[0], CARDINAL]; rope _ Rope.FromProc[len, GetChar]; }; s: REF GFileFormatDefs.GFileStyle _ NEW[GFileFormatDefs.GFileStyle]; ReadStructure[diskHandle, LOOPHOLE[s], GFileFormatDefs.lGFileStyle]; style _ NEW[StyleRep]; style^ _ [ color: [hue: s.hue, saturation: s.saturation, brightness: s.brightness], areaColor: [hue: s.ahue, saturation: s.asaturation, brightness: s.abrightness], textBackgroundColor: [hue: s.bhue, saturation: s.bsaturation, brightness: s.bbrightness], fillArea: s.afilled, outlineArea: s.aoutlined, useTextBackground: s.background, beginning: MakeEnd[s.send, s.bdx, s.bdy, s.ba, s.bb, s.bc], end: MakeEnd[s.eend, s.edx, s.edy, s.ea, s.eb, s.ec], thickness: ConvertReal[s.thickness], font: s.fontid-1, --???? dashedness: s.dashedness, anchor: LOOPHOLE[s.anchor], junction: LOOPHOLE[s.junction], textRotation: LOOPHOLE[s.torient], styleName: Name[s.stylename] ]; }; initialCurvePart: CurvePart ~ [cyclicSpline [[naturalUM, NIL]]]; initialVariant: Variant ~ [curve [initialCurvePart]]; initialObjectRep: ObjectRep ~ [TRUE, inVisible, 0, 0, initialVariant]; ReadObjects: INTERNAL UNSAFE PROC [diskHandle: IO.STREAM] RETURNS [objects: Objects] ~ UNCHECKED { <> ReadObject: INTERNAL UNSAFE PROC RETURNS [object: Object _ NEW[ObjectRep _ initialObjectRep]] ~ UNCHECKED { ReadVariant: INTERNAL UNSAFE PROC RETURNS [variant: Variant _ initialVariant] ~ UNCHECKED { ReadVEC: INTERNAL UNSAFE PROC RETURNS [vec: VEC] ~ UNCHECKED { point: GFileFormatDefs.GFilePoint; ReadStructure[diskHandle, @point, GFileFormatDefs.lGFilePoint]; vec.x _ ConvertReal[point.x]; vec.y _ ConvertReal[point.y]; }; ReadCurvePart: INTERNAL UNSAFE PROC RETURNS [cp: CurvePart _ initialCurvePart] ~ UNCHECKED { ReadLink: INTERNAL UNSAFE PROC RETURNS [link: Link] ~ UNCHECKED { <
> knotWord: GFileFormatDefs.GFileKnotWord; ReadStructure[diskHandle, @knotWord, GFileFormatDefs.lGFileKnotWord]; IF knotWord.knotcount=0 THEN ERROR; --Bad link link _ NEW[LinkRep[knotWord.knotcount]]; link.degree _ LOOPHOLE[knotWord.knottype]; FOR knot: CARDINAL IN [0..link.nKnots) DO link.knots[knot] _ ReadVEC[]; ENDLOOP; }; --ReadLink nLinks: CARDINAL _ ReadCardinal[diskHandle]; <
> SELECT gObject.trajtype FROM GFileFormatDefs.typeLinkedTraj => { linkPart: LinkPart _ NEW[LinkPartRep[nLinks]]; linkPart.splineType _ ConvertSplineType[gObject.splinetype]; FOR link: CARDINAL IN [0..nLinks) DO linkPart.links[link] _ ReadLink[]; ENDLOOP; cp _ [linked [linkPart]]; }; GFileFormatDefs.typeCSTraj => { IF nLinks#1 THEN ERROR --Impossible cyclic spline ELSE { splineType: SplineType _ ConvertSplineType[gObject.splinetype]; SELECT splineType FROM naturalUM => splineType _ cyclicUM; naturalAL => splineType _ cyclicAL; ENDCASE; cp _ [cyclicSpline [[splineType, ReadLink[]]]]; }; }; ENDCASE => ERROR; --Invalid trajectory }; --ReadCurvePart ReadCaptionPart: INTERNAL UNSAFE PROC RETURNS [cp: CaptionPart] ~ UNCHECKED { ReadString: PROCEDURE RETURNS [r: ROPE] ~ TRUSTED { length: CARDINAL _ LOOPHOLE[IO.GetChar[diskHandle], CARDINAL]; t: REF TEXT _ NEW[TEXT[length]]; IF IO.GetBlock[diskHandle,t,0,length] # length THEN ERROR; --Possible disk problem r _ Rope.FromRefText[t]; <> IF (length+1) MOD 2 # 0 THEN [] _ IO.GetChar[diskHandle]; }; cp.position _ ReadVEC[]; cp.text _ ReadString[]; }; <
> SELECT gObject.objtype FROM GFileFormatDefs.typeCurveObject => { variant _ [curve [ReadCurvePart[]]]; }; GFileFormatDefs.typeAreaObject => { variant _ [area [ReadCurvePart[]]]; }; GFileFormatDefs.typeCaptionObject => { variant _ [caption [ReadCaptionPart[]]]; }; ENDCASE => ERROR; }; --ReadVariant gObject: REF GFileFormatDefs.GFileObject _ NEW[GFileFormatDefs.GFileObject]; <
> ReadStructure[diskHandle, LOOPHOLE[gObject], GFileFormatDefs.lGFileObject]; object^ _ [ hidden: gObject.hidewindow, visibility: LOOPHOLE[gObject.visible], style: gObject.style-1, --Fence-post correction cluster: gObject.cluster, variant: ReadVariant[] ]; }; --ReadObject objectCount: CARDINAL ~ ReadCardinal[diskHandle]; <
> objects _ NEW[ObjectsRep[objectCount]]; FOR index: CARDINAL IN [0..objectCount) DO objects[index] _ ReadObject[]; ENDLOOP; }; --ReadObjects ConvertSplineType: PROC [splineTypeInFile: CARDINAL] RETURNS [SplineType] ~ { <> RETURN [SELECT splineTypeInFile FROM IN [GFileFormatDefs.typeNUMSpline..GFileFormatDefs.typeCRSpline] => LOOPHOLE[splineTypeInFile], ENDCASE => ERROR --Illegal spline type ]; }; charsPerPage: CARDINAL ~ 512; MoveToSector: INTERNAL UNSAFE PROC [diskHandle: IO.STREAM, si: GFileFormatDefs.SectorIndex] ~ UNCHECKED { IO.SetIndex[diskHandle, INTEGER[si*charsPerPage]]; }; ReadStructure: INTERNAL UNSAFE PROC [diskHandle: IO.STREAM, p: LONG POINTER, l: CARDINAL] ~ UNCHECKED { from: LONG POINTER; b: REF TEXT _ NEW[TEXT[l*2]]; IF IO.GetBlock[diskHandle, b, 0, 2*l] # 2*l THEN ERROR; from _ LOOPHOLE[b,LONG POINTER]+2; --start of bytes PrincOpsUtils.LongCopy[from,l,p]; }; ReadCardinal: INTERNAL UNSAFE PROC [diskHandle: IO.STREAM] RETURNS [CARDINAL] ~ UNCHECKED { high, low: CHARACTER; word: Basics.BytePair; high _ IO.GetChar[diskHandle]; low _ IO.GetChar[diskHandle]; word.high _ LOOPHOLE[high]; word.low _ LOOPHOLE[low]; RETURN[LOOPHOLE[word]]; }; ReadHeader: INTERNAL UNSAFE PROC [diskHandle: IO.STREAM] RETURNS [header: GFileFormatDefs.GFileHeader] ~ UNCHECKED { h: POINTER TO GFileFormatDefs.GFileHeader ~ @header; IO.SetIndex[diskHandle,0]; ReadStructure[diskHandle, h, GFileFormatDefs.lGFileHeader]; ConvertReal _ IF (h.majversion=1 AND h.minversion IN [0..3]) THEN ConvertRealFromMesa5 ELSE ConvertRealFromReal; <> <> }; FixFileName: INTERNAL PROC [oldname, extension: ROPE] RETURNS [newname:ROPE] ~ { cp: FS.ComponentPositions; dirOmitted: BOOL; [newname, cp, dirOmitted] _ FS.ExpandName[oldname]; newname _ Rope.Cat[Rope.Substr[base: newname, len: cp.base.start+cp.base.length], extension]; IF dirOmitted THEN newname _ Rope.Substr[base: newname, start: cp.base.start]; }; CachedFont: TYPE ~ RECORD [ key: REF GriffinImageUtils.Font, imagerFont: Imager.Font ]; CompareFonts: PROC [k, data: REF] RETURNS [c: Basics.Comparison] ~ { f1: REF GriffinImageUtils.Font ~ NARROW[k]; f2: REF GriffinImageUtils.Font ~ NARROW[data, REF CachedFont].key; SELECT TRUE FROM (c _ Basics.CompareCard[f1.points, f2.points])#equal => RETURN; (c _ Basics.CompareCard[f1.face, f2.face])#equal => RETURN; (c _ Basics.CompareCard[f1.rotation, f2.rotation])#equal => RETURN; ENDCASE => RETURN [Rope.Compare[f1.name, f2.name, FALSE]]; }; GetFontsKey: PROC [data: REF] RETURNS [key: REF] ~ { RETURN [NARROW[data, REF CachedFont].key]; }; fontCache: RedBlackTree.Table ~ RedBlackTree.Create[GetFontsKey, CompareFonts]; FindFont: PROC [font: GriffinImageUtils.Font] RETURNS [imagerFont: Imager.Font _ NIL] ~ { key: REF GriffinImageUtils.Font _ NEW[GriffinImageUtils.Font _ font]; cachedFont: REF CachedFont ~ NARROW[RedBlackTree.Lookup[fontCache, key]]; IF cachedFont#NIL THEN RETURN [cachedFont.imagerFont]; { --Here, it wasn't in the cache FaceToExtension: PROC RETURNS [extension: ROPE] ~ { RETURN [ SELECT font.face FROM 0 => "-mrr", --Plain 1 => "-mir", --Italics 2 => "-brr", --Bold 3 => "-bir", --Italics + Bold ENDCASE => ERROR ] }; msg: ROPE _ NIL; --Complaint about fonts fontName: ROPE _ font.name; fontExtension: ROPE _ FaceToExtension[]; <> WHILE imagerFont=NIL DO imagerFont _ ImagerFont.Find[Rope.Cat["Xerox/PressFonts/", fontName, fontExtension] ! Imager.Error => { SELECT FALSE FROM --Various recovery techniques. Note that the result order is to (1) try the font as specified, (2) look for a plain-faced version of the font, (3) look for Helvetica, but use the bold and/or italics, (4) use plain Helvetica, (5) give up and die. Rope.Equal["-mrr", fontExtension] => { fontExtension _ "-mrr"; }; Rope.Equal["Helvetica", fontName] => { fontName _ "Helvetica"; fontExtension _ FaceToExtension[]; -- }; ENDCASE => GOTO Fail; --Leave the error uncaught imagerFont _ NIL; msg _ Rope.Cat[error.explanation, " Substituting Xerox/PressFonts/", fontName, fontExtension, "."]; CONTINUE; EXITS Fail => NULL; }]; ENDLOOP; IF msg#NIL THEN MessageWindow.Append[msg, TRUE]; <> imagerFont _ ImagerFont.Scale[font: imagerFont, s: PointsToFontSize[font.points]]; RedBlackTree.Insert[self: fontCache, dataToInsert: NEW[CachedFont _ [key, imagerFont]], insertKey: key]; }; }; PointsToFontSize: PROC [points: REAL] RETURNS [fontSize: REAL] ~ INLINE { RETURN [points*Imager.micasPerPoint] }; GriffinObjectToImagerCalls: PUBLIC PROC [context: Imager.Context, object: Object, style: Style, fonts: FontDir] ~ CHECKED { SetColor: PROC [color: Color] ~ { Imager.SetColor[context, ImagerColor.ColorFromRGB[ImagerColor.RGBFromHSV[[H: color.hue/255.0, S: color.saturation/255.0, V: color.brightness/255.0]]]]; }; SetThickness: PROC [thickness: REAL] ~ { Imager.SetStrokeWidth[context, thickness]; }; PlayCurvePart: PROC [cp: CurvePart, closed: BOOL] ~ { path: ImagerPath.Trajectory; first: BOOLEAN _ TRUE; moveTo: ImagerPath.MoveToProc _ firstMoveTo; nullMoveTo: ImagerPath.MoveToProc = {}; firstMoveTo: ImagerPath.MoveToProc = TRUSTED { path _ ImagerPath.MoveTo[p]; moveTo _ nullMoveTo}; curveTo: ImagerPath.CurveToProc = {path _ ImagerPath.CurveTo[path, p1, p2, p3]}; linkToCubicPath: PROC [cubicLink: Link, splineType: CubicSplines.SplineType] RETURNS[cubicPath: CubicPaths.Path] = { cyclic: BOOLEAN _ splineType=cyclicAL OR splineType=cyclicUM; nKnots: CARDINAL _ IF cyclic THEN cubicLink.nKnots+1 ELSE cubicLink.nKnots; knots: CubicSplines.KnotSequence _ NEW[CubicSplines.KnotSequenceRec[nKnots]]; FOR i: CARDINAL IN [0..cubicLink.nKnots) DO knots[i][CubicSplines.X] _ cubicLink[i].x; knots[i][CubicSplines.Y] _ cubicLink[i].y; ENDLOOP; IF cyclic THEN { knots[cubicLink.nKnots][CubicSplines.X] _ cubicLink[0].x; knots[cubicLink.nKnots][CubicSplines.Y] _ cubicLink[0].y; }; cubicPath _ CubicPaths.PathFromCubic[CubicSplines.MakeSpline[knots, splineType]]; RETURN[cubicPath]; }; WITH cp SELECT FROM links: linked CurvePart => { --degree=1 for lines, 3 for cubics. splineType: CubicSplines.SplineType _ links.linked.splineType; FOR linkIndex: CARDINAL IN [0..links.linked.nLinks) DO link: Link ~ links.linked.links[linkIndex]; IF link.degree=3 THEN { --cubic cubicPath: CubicPaths.Path _ linkToCubicPath[link, splineType]; CubicPaths.EnumeratePath[cubicPath, moveTo, curveTo]; } ELSE { --assume lines moveTo[link[0]]; FOR knotIndex: CARDINAL IN [1..link.nKnots) DO path _ ImagerPath.LineTo[path, link[knotIndex]]; ENDLOOP; }; ENDLOOP; IF closed THEN path _ ImagerPath.LineTo[path, links.linked.links[0].knots[0]]; }; cyclic: cyclicSpline CurvePart => { cubicPath: CubicPaths.Path _ linkToCubicPath[cyclic.cyclicSpline.link, cyclic.cyclicSpline.splineType]; CubicPaths.EnumeratePath[cubicPath, moveTo, curveTo]; }; ENDCASE => ERROR; IF closed AND style.fillArea THEN { SetColor[style.areaColor]; Imager.MaskFillTrajectory[context, path]; }; IF ~closed OR style.outlineArea THEN { SetColor[style.color]; Imager.SetStrokeWidth[context, style.thickness]; Imager.MaskStrokeTrajectory[context, path]; }; }; WITH object.variant SELECT FROM caption: caption Variant => { imagerFont: Imager.Font ~ FindFont[fonts[style.font]]; length: REAL; SetColor[style.color]; Imager.SetFont[context, imagerFont]; length _ ImagerFont.RopeWidth[imagerFont, caption.caption.text].x; Imager.SetXY[context, caption.caption.position]; Imager.SetYRel[context, -PointsToFontSize[fonts[style.font].points]]; Imager.Move[context]; SELECT style.textRotation FROM d90 => Imager.RotateT[context, 90]; d180 => Imager.RotateT[context, 180]; d270 => Imager.RotateT[context, 270]; ENDCASE; SELECT style.anchor FROM left => NULL; right => Imager.SetXRel[context, -length]; center => Imager.SetXRel[context, -length/2]; ENDCASE => ERROR; Imager.ShowRope[context, caption.caption.text]; }; curve: curve Variant => PlayCurvePart[curve.curve, FALSE]; area: area Variant => PlayCurvePart[area.curve, TRUE]; ENDCASE => ERROR; }; GriffinToImagerCalls: PUBLIC PROC [context: Imager.Context, g: GriffinImage] ~ CHECKED { FOR fIndex: CARDINAL IN [FIRST[ValidFigRange]..g.nFigures) DO figure: Figure ~ g.figures[fIndex]; FOR oIndex: CARDINAL IN [0..figure.objects.nObjects) DO PlayObject: PROC ~ { GriffinObjectToImagerCalls[context, object, style, figure.font]; }; object: Object ~ figure.objects[oIndex]; style: Style ~ figure.styles[object.style]; IF ~object.hidden THEN Imager.DoSaveAll[context, PlayObject]; ENDLOOP; ENDLOOP; }; END.