GriffinImageUtilsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Eric Nickell, July 31, 1985 9:43:40 am PDT
Stone, August 6, 1985 0:20:31 am PDT
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 {
Monitors the current definition of ConvertReal
ENABLE UNWIND => NULL;
Name: UNSAFE PROC [chars: PACKED ARRAY [0 .. GFileFormatDefs.cNameChars] OF CHARACTER] RETURNS [rope: ROPENIL] ~ {
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 {
Reads the fth figure into the GriffinImage structure.
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: ROPENIL] ~ 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: ROPENIL] ~ 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 {
N.B.: Does conversion on the REALs found
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: ROPENIL] ~ 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 {
NOTE: diskHandle must be set up ready to read the object count!!!!!!!!!!!!!
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 {
Main body of ReadCurvePart
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];
Main body of ReadCurvePart
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: CARDINALLOOPHOLE[IO.GetChar[diskHandle], CARDINAL];
t: REF TEXTNEW[TEXT[length]];
IF IO.GetBlock[diskHandle,t,0,length] # length THEN ERROR; --Possible disk problem
r ← Rope.FromRefText[t];
always read an even number of bytes: string length + string chars MOD 2 = 0
IF (length+1) MOD 2 # 0 THEN [] ← IO.GetChar[diskHandle];
};
cp.position ← ReadVEC[];
cp.text ← ReadString[];
};
Main body of ReadVariant
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];
Main body of ReadObject
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];
Main body of ReadObjects
objects ← NEW[ObjectsRep[objectCount]];
FOR index: CARDINAL IN [0..objectCount) DO
objects[index] ← ReadObject[];
ENDLOOP;
}; --ReadObjects
ConvertSplineType: PROC [splineTypeInFile: CARDINAL] RETURNS [SplineType] ~ {
Dependent on definition of SplineType: TYPE ~ {naturalUM,cyclicUM,naturalAL,cyclicAL,bezier,bsplineInterp,bspline,crspline};
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 TEXTNEW[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;
Version 1.4 is the first with the Ieee floating point format.
We need to set up to convert the reals for older versions.
};
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: ROPENIL;  --Complaint about fonts
fontName: ROPE ← font.name;
fontExtension: ROPE ← FaceToExtension[];
Look up the font
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];
Scale and rotate (ignored for now) the font
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: BOOLEANTRUE;
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: CARDINALIF 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.