DIRECTORY Environment, PDGraphics, IPImagerBasic, IPScan, PDFileWriter, Real, RealFns, Rope, XFontCache, PressFontReader ; PDGraphicsImpl: CEDAR PROGRAM IMPORTS IPScan, PDFileWriter, Real, RealFns, Rope, XFontCache, PressFontReader EXPORTS PDGraphics = BEGIN tryLeftoverMode: BOOLEAN _ TRUE; tryBandSize: NAT _ 16; Pair: TYPE = IPImagerBasic.Pair; InkWell: TYPE = ARRAY [0..16) OF PACKED ARRAY [0..16) OF [0..1]; Toner: TYPE = PDFileWriter.Toner; Context: TYPE = PDGraphics.Context; Transform: TYPE = PDGraphics.Transform; PathItem: TYPE = PDGraphics.PathItem; InkWellGray: PROC [gray: [0..256]] RETURNS [inkWell: InkWell] = { i, j: NAT _ 0; FOR i: NAT IN [0..16) DO inkWell[i] _ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; ENDLOOP; WHILE gray>0 DO inkWell[i][j] _ 0; i _ i+1; IF i>=16 THEN {i_i-16; j _ j+1}; j _ j+3; IF j>=16 THEN j_j-16; gray_gray-1; ENDLOOP; }; InkWellGray45: PROC [gray: [0..256]] RETURNS [inkWell: InkWell] = { i, j: INTEGER _ 0; FOR i: NAT IN [0..16) DO inkWell[i] _ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; ENDLOOP; WHILE gray>0 DO inkWell[i][j] _ 0; i _ i+1; IF i>=16 THEN {i_i-16; j _ j+1}; j _ j-1; IF j<0 THEN j_j+16; gray_gray-1; ENDLOOP; }; InkWellGrayS: PROC [gray: [0..256]] RETURNS [inkWell: InkWell] = { i, j: INTEGER _ 0; FOR i: NAT IN [0..16) DO inkWell[i] _ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; ENDLOOP; WHILE gray>0 DO inkWell[i][j] _ 0; i _ i+3; IF i>=16 THEN {i_i-16; j _ j-1}; IF j<0 THEN j_j+16; gray_gray-1; ENDLOOP; }; trc: ARRAY [0..64) OF [0..256] _ InitTRC[]; InitTRC: PROC RETURNS [trc: ARRAY [0..64) OF [0..256]] = { FOR i: [0..64) IN [0..64) DO trc[i] _ Real.RoundLI[i/63.0*256.0]; ENDLOOP; }; dummyLoadReference: PDFileWriter.LoadReference = LAST[PDFileWriter.LoadReference]; MakeGray: PROC [context: Context, toner: Toner, index: [0..64)] = { value: [0..256] _ trc[index]; inkWell: InkWell _ SELECT toner FROM yellow => InkWellGrayS[value], magenta => InkWellGray45[value], ENDCASE => InkWellGray[value]; bitsPtr: LONG POINTER; loadReference: PDFileWriter.LoadReference; TRUSTED {bitsPtr _ @inkWell}; loadReference _ context.pdState.LoadContiguousColorTile[phase: 0, sMin: 0, fMin: 0, sSize: 16, fSize: 16, bitsPtr: bitsPtr]; context.grayTileRef[toner][index] _ loadReference; }; SetGray: PUBLIC PROC [context: Context, gray: REAL] = { index: [0..64) _ MAX[MIN[Real.RoundLI[gray*63], 63], 0]; SetValueForToner: PROC [toner: Toner] = { IF context.grayTileRef[toner][index] = dummyLoadReference THEN MakeGray[context, toner, index]; context.pdState.SetColorTile[toner, context.grayTileRef[toner][index]] }; context.pdState.DoForEachToner[SetValueForToner]; }; ToRange: PROC [v: REAL] RETURNS [REAL] = {RETURN[MAX[MIN[v, 1.0], 0.0]]}; HSVToRGB: PROC [h, s, v: REAL] RETURNS [r, g, b: REAL] = { hue: REAL _ ToRange[h]; saturation: REAL _ ToRange[s]; value: REAL _ ToRange[v]; ihue: INTEGER; fhue,m,n,k: REAL; hue _ hue*6; ihue _ Real.FixI[hue]; --integer hue fhue _ hue-ihue; --fractional hue IF ihue=6 THEN ihue _ 0; m _ value*(1-saturation); n _ value*(1-(saturation*fhue)); k _ value*(1-(saturation*(1-fhue))); SELECT ihue FROM 0 => RETURN[value,k,m]; 1 => RETURN[n,value,m]; 2 => RETURN[m,value,k]; 3 => RETURN[m,n,value]; 4 => RETURN[k,m,value]; 5 => RETURN[value,m,n]; ENDCASE => RETURN[0,0,0]; }; SetColor: PUBLIC PROC [context: Context, hue, saturation, value: REAL] = { r, g, b: REAL; SetValueForToner: PROC [toner: Toner] = { i: REAL _ SELECT toner FROM black => value, cyan => r, magenta => g, yellow => b, ENDCASE => ERROR; index: [0..64) _ MAX[MIN[Real.RoundLI[i*63], 63], 0]; IF context.grayTileRef[toner][index] = dummyLoadReference THEN MakeGray[context, toner, index]; context.pdState.SetColorTile[toner, context.grayTileRef[toner][index]] }; [r, g, b] _ HSVToRGB[hue, saturation, value]; context.pdState.DoForEachToner[SetValueForToner]; }; SetBlack: PUBLIC PROC [context: Context] = { DoToner: PROC [toner: Toner] = {context.pdState.SetColorInk[toner]}; context.pdState.DoForEachToner[DoToner]; }; SetWhite: PUBLIC PROC [context: Context] = { DoToner: PROC [toner: Toner] = {context.pdState.SetColorClear[toner]}; context.pdState.DoForEachToner[DoToner]; }; SetFunnyGray: PUBLIC PROC [context: Context, gray: REAL, toner: Toner] = { sSize: NAT ~ 4; fSize: NAT ~ 9; fSizeRoundedUpToWord: NAT ~ 16; phase: NAT ~ 3; bits: ARRAY [0..sSize) OF PACKED ARRAY [0..fSizeRoundedUpToWord) OF [0..1]; nLevels: NAT ~ sSize * fSize; brick: ARRAY [0..sSize) OF PACKED ARRAY [0..fSizeRoundedUpToWord) OF [0..nLevels] ~ [ [ 08, 11, 21, 24, 28, 33, 26, 22, 10 ,0,0,0,0,0,0,0], [ 12, 17, 25, 34, 03, 05, 30, 27, 23 ,0,0,0,0,0,0,0], [ 29, 35, 31, 15, 01, 02, 07, 36, 32 ,0,0,0,0,0,0,0], [ 20, 18, 16, 14, 09, 04, 06, 13, 19 ,0,0,0,0,0,0,0] ]; level: [0..nLevels] _ MAX[MIN[Real.RoundLI[gray*nLevels], nLevels], 0]; bitsPtr: LONG POINTER; loadReference: PDFileWriter.LoadReference; FOR s: NAT IN [0..sSize) DO FOR f: NAT IN [0..fSizeRoundedUpToWord) DO bits[s][f] _ IF level >= brick[s][f] THEN 0 ELSE 1; ENDLOOP; ENDLOOP; TRUSTED {bitsPtr _ @bits}; loadReference _ context.pdState.LoadContiguousColorTile[phase: phase, sMin: 0, fMin: 0, sSize: sSize, fSize: fSize, bitsPtr: bitsPtr]; context.pdState.SetColorTile[toner, loadReference]; }; NewHornetContext: PUBLIC PROC [fileName: Rope.ROPE] RETURNS [context: Context] = { GenPageBounds: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { move[[0,0]]; line[[0, 3264]]; line[[4224, 3264]]; line[[4224, 0]]; }; context _ NEW[PDGraphics.ContextRec]; context.transform.a _ context.transform.d _ 0; context.transform.b _ context.transform.c _ 384/72.0; context.pdState _ PDFileWriter.Create[fileName: fileName, deviceCode: hornet, sResolution: 384, fResolution: 384, imageSSize: 3264, imageFSize: 4224, nColors: 1, bandSSize: tryBandSize, leftOverMode: tryLeftoverMode, priorityImportant: tryLeftoverMode]; context.devicePath _ IPScan.Allocate[]; context.devicePath.PushPath[GenPageBounds]; FOR toner: Toner IN Toner DO FOR i: [0..64) IN [0..64) DO context.grayTileRef[toner][i] _ dummyLoadReference ENDLOOP ENDLOOP; context.fontCache _ XFontCache.Create[]; }; NewPuffinContext: PUBLIC PROC [fileName: Rope.ROPE] RETURNS [context: Context] = { GenPageBounds: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { move[[0,0]]; line[[0, 4224]]; line[[3264, 4224]]; line[[3264, 0]]; }; context _ NEW[PDGraphics.ContextRec]; context.transform.d _ -(context.transform.a _ 384/72.0); context.transform.f _ 4224; context.transform.b _ context.transform.c _ 0; context.pdState _ PDFileWriter.Create[fileName: fileName, deviceCode: puffin, sResolution: 384, fResolution: 384, imageSSize: 4224, imageFSize: 3264, nColors: 3, bandSSize: tryBandSize, leftOverMode: tryLeftoverMode, priorityImportant: tryLeftoverMode]; context.devicePath _ IPScan.Allocate[]; context.devicePath.PushPath[GenPageBounds]; FOR toner: Toner IN Toner DO FOR i: [0..64) IN [0..64) DO context.grayTileRef[toner][i] _ dummyLoadReference ENDLOOP ENDLOOP; context.fontCache _ XFontCache.Create[]; }; TransformPoint: PUBLIC PROC [context: Context, p: Pair] RETURNS [device: Pair] = { device.x _ context.transform.a * p.x + context.transform.c * p.y + context.transform.e; device.y _ context.transform.b * p.x + context.transform.d * p.y + context.transform.f; }; TransformVector: PUBLIC PROC [context: Context, v: Pair] RETURNS [device: Pair] = { device.x _ context.transform.a * v.x + context.transform.c * v.y; device.y _ context.transform.b * v.x + context.transform.d * v.y; }; Concat: PUBLIC PROC [context: Context, transform: Transform] = { new: Transform; new.a _ transform.a * context.transform.a + transform.b * context.transform.c; new.b _ transform.a * context.transform.b + transform.b * context.transform.d; new.c _ transform.c * context.transform.a + transform.d * context.transform.c; new.d _ transform.c * context.transform.b + transform.d * context.transform.d; new.e _ transform.e * context.transform.a + transform.f * context.transform.c + context.transform.e; new.f _ transform.e * context.transform.b + transform.f * context.transform.d + context.transform.f; context.transform _ new; }; Translate: PUBLIC PROC [context: Context, v: Pair] = {Concat[context, [e: v.x, f: v.y]]}; Scale: PUBLIC PROC [context: Context, s: REAL] = {Concat[context, [a: s, d: s]]}; Rotate: PUBLIC PROC [context: Context, degrees: REAL] = {s: REAL ~ RealFns.SinDeg[degrees]; c: REAL ~ RealFns.CosDeg[degrees]; Concat[context, [c, s, -s, c, 0, 0]]}; MoveTo: PUBLIC PROC [context: Context, x, y: REAL] = {context.path _ CONS[[move, TransformPoint[context, [x, y]]], context.path]}; LineTo: PUBLIC PROC [context: Context, x, y: REAL] = {context.path _ CONS[[knot, TransformPoint[context, [x, y]]], context.path]}; CurveTo: PUBLIC PROC [context: Context, x1, y1, x2, y2, x3, y3: REAL] = { context.path _ CONS[[control, TransformPoint[context, [x1, y1]]], context.path]; context.path _ CONS[[control, TransformPoint[context, [x2, y2]]], context.path]; context.path _ CONS[[knot, TransformPoint[context, [x3, y3]]], context.path] }; Reverse: PROC [list: LIST OF PathItem] RETURNS [LIST OF PathItem] ~ { new: LIST OF PathItem _ NIL; WHILE list # NIL DO current: LIST OF PathItem _ list; list _ list.rest; current.rest _ new; new _ current; ENDLOOP; RETURN [new] }; DrawArea: PUBLIC PROC [context: Context] = { DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { FOR s: INTEGER IN [y..y+h) DO SendRun[sMin: s, fMin: x, fSize: w]; ENDLOOP; }; context.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: 0, ymax: context.pdState.GetBounds.sMax]; }; ClipArea[context, FALSE]; context.pdState.MaskRunGroup[DeliverRuns]; context.devicePath.PopPath; }; ClipArea: PUBLIC PROC [context: Context, exclude: BOOLEAN] = { path: LIST OF PathItem _ Reverse[context.path]; GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { WHILE path # NIL DO SELECT path.first.pathItemType FROM move => move[path.first.pair]; knot => line[path.first.pair]; control => { curve[path.first.pair, path.rest.first.pair, path.rest.rest.first.pair]; path _ path.rest.rest }; ENDCASE => ERROR; path _ path.rest; ENDLOOP; }; context.path _ NIL; context.devicePath.PushPath[GenPath, exclude]; context.clippers _ context.clippers+1; }; PopClipper: PUBLIC PROC [context: Context] = { context.devicePath.PopPath; context.clippers _ context.clippers-1; }; CharRepresentation: TYPE = {runGroup, raster}; CharLoadInfo: TYPE = RECORD [loadRef: PDFileWriter.LoadReference, sMin, fMin, sMax, fMax: INTEGER, representation: CharRepresentation]; stats: RECORD [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT _ 0]; Stats: PROC RETURNS [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT] = { loadRunGroups _ stats.loadRunGroups; stats.loadRunGroups _ 0; loadRasters _ stats.loadRasters; stats.loadRasters _ 0; rasterChars _ stats.rasterChars; stats.rasterChars _ 0; runGroupChars _ stats.runGroupChars; stats.runGroupChars _ 0; clippedChars _ stats.clippedChars; stats.clippedChars _ 0; culledChars _ stats.culledChars; stats.culledChars _ 0; }; Visibility: TYPE = {all, part, none}; Floor: PROC[r: REAL] RETURNS [i: INT] = {i _ Real.RoundLI[r]; WHILE i>r DO i_i-1 ENDLOOP}; Ceiling: PROC[r: REAL] RETURNS [i: INT] = {i _ Real.RoundLI[r]; WHILE i= pageSmax OR sMin + sSize <= 0 OR fMin >= pageFmax OR fMin + fSize <= 0 THEN RETURN[none]; IF context.clippers = 0 THEN RETURN [ IF sMin >= 0 AND sMin + sSize <= pageSmax AND fMin >= 0 AND fMin + fSize <= pageFmax THEN all ELSE part ] ELSE { v: Visibility _ all; xMin: INTEGER _ Floor[fMin]; xMax: INTEGER _ Ceiling[fMin+fSize]; yMin: INTEGER _ Floor[sMin]; yMax: INTEGER _ Ceiling[sMin+sSize]; curY: INTEGER _ yMin; GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { move[[xMin, yMin]]; line[[xMax, yMin]]; line[[xMax, yMax]]; line[[xMin, yMax]]; }; CheckVisible: PROC [x, y, w, h: INTEGER] = { IF x # xMin OR x + w # xMax THEN v _ part; IF curY # y THEN v _ part; curY _ curY + h; }; context.devicePath.PushPath[GenPath]; context.devicePath.ScanConvert[proc: CheckVisible, ymin: yMin, ymax: yMax]; context.devicePath.PopPath; IF curY = yMin THEN v _ none ELSE IF curY # yMax THEN v _ part; RETURN[v]; }; }; rasterToRunGroupStorageRatio: INT _ 1; DrawChars: PUBLIC PROC [context: Context, font: PressFontReader.Font, map: PROC[PROC[CHAR]]] = { fontCache: XFontCache.FontCache ~ context.fontCache; devicePath: IPScan.DevicePath _ NIL; a: REAL ~ context.transform.a; b: REAL ~ context.transform.b; c: REAL ~ context.transform.c; d: REAL ~ context.transform.d; Loophole: PROC [font: PressFontReader.Font] RETURNS [REF] ~ TRUSTED {RETURN[LOOPHOLE[font]]}; f: REF ~ Loophole[font]; -- because font^ is READONLY fontCode: XFontCache.FontCode ~ XFontCache.GetFontCode[a, b, c, d, f]; Load: PROC [char: CHAR] = { XForm: PROC [x, y: REAL] RETURNS [p: Pair] = { p.x _ a * x + c * y; p.y _ b * x + d * y; }; GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { moveTo: PROCEDURE [x, y: REAL] = {move[XForm[x, y]]}; lineTo: PROCEDURE [x, y: REAL] = {line[XForm[x, y]]}; curveTo: PROCEDURE [x1, y1, x2, y2, x3, y3: REAL] = {curve[XForm[x1, y1], XForm[x2, y2], XForm[x3, y3]]}; drawArea: PROCEDURE = {}; TRUSTED{font.GetCharOutline[char, moveTo, lineTo, curveTo, drawArea]}; }; FindBB: PROC [x, y, w, h: INTEGER] = { IF loadInfo.sMin > y THEN loadInfo.sMin _ y; IF loadInfo.fMin > x THEN loadInfo.fMin _ x; IF loadInfo.sMax < y+h THEN loadInfo.sMax _ y+h; IF loadInfo.fMax < x+w THEN loadInfo.fMax _ x+w; nRuns _ nRuns + 1; }; DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { f: CARDINAL ~ x-loadInfo.fMin; FOR s: INTEGER IN [y-loadInfo.sMin..y+h-loadInfo.sMin) DO SendRun[sMin: s, fMin: f, fSize: w]; ENDLOOP; }; devicePath.ScanConvert[BoxFromScanConverter, loadInfo.sMin, loadInfo.sMax, TRUE]; }; loadInfo: REF CharLoadInfo _ NEW[CharLoadInfo]; nRuns: INT _ 0; IF devicePath = NIL THEN devicePath _ IPScan.Allocate[]; devicePath.PushPath[GenPath]; loadInfo.sMin _ loadInfo.fMin _ LAST[INTEGER]; loadInfo.sMax _ loadInfo.sMax _ FIRST[INTEGER]; devicePath.ScanConvert[proc: FindBB, parityFill: TRUE]; IF loadInfo.sMin>loadInfo.sMax THEN loadInfo.sMin _ loadInfo.sMax _ loadInfo.fMin _ loadInfo.fMax _ 0; IF loadInfo.fMax-loadInfo.fMin > 32*Environment.bitsPerWord OR (nRuns * 2 + 2)*rasterToRunGroupStorageRatio < INT[loadInfo.sMax-loadInfo.sMin] * INT[(loadInfo.fMax-loadInfo.fMin+15)/16] + 2 THEN { loadInfo.loadRef _ context.pdState.LoadRunGroup[DeliverRuns]; loadInfo.representation _ runGroup; stats.loadRunGroups _ stats.loadRunGroups + 1; } ELSE { lineBuf: PACKED ARRAY [0..32*Environment.bitsPerWord) OF [0..1] _ ALL[0]; curY: INT _ loadInfo.sMin; DeliverLines: PROC [SendScanLine: PROC [p: LONG POINTER]] = { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { f: CARDINAL ~ x-loadInfo.fMin; IF h # 1 THEN ERROR; WHILE curY < y DO TRUSTED {SendScanLine[@lineBuf]}; lineBuf _ ALL[0]; curY _ curY + 1; ENDLOOP; FOR f: [0..32*Environment.bitsPerWord) IN [x-loadInfo.fMin..x-loadInfo.fMin+w) DO lineBuf[f] _ 1; ENDLOOP; }; devicePath.ScanConvert[BoxFromScanConverter, loadInfo.sMin, loadInfo.sMax, TRUE]; WHILE curY < loadInfo.sMax DO TRUSTED {SendScanLine[@lineBuf]}; lineBuf _ ALL[0]; curY _ curY + 1; ENDLOOP; }; loadInfo.loadRef _ context.pdState.LoadSampleArray[loadInfo.sMax-loadInfo.sMin, loadInfo.fMax-loadInfo.fMin, DeliverLines]; loadInfo.representation _ raster; stats.loadRasters _ stats.loadRasters + 1; }; devicePath.PopPath; fontCache.LoadCharData[fontCode, char, loadInfo]; }; DoChar: PROC [char: CHAR] = { e: REAL ~ context.transform.e; f: REAL ~ context.transform.f; XForm: PROC [x, y: REAL] RETURNS [p: Pair] = { p.x _ a * x + c * y + e; p.y _ b * x + d * y + f; }; GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { moveTo: PROCEDURE [x, y: REAL] = {move[XForm[x, y]]}; lineTo: PROCEDURE [x, y: REAL] = {line[XForm[x, y]]}; curveTo: PROCEDURE [x1, y1, x2, y2, x3, y3: REAL] = {curve[XForm[x1, y1], XForm[x2, y2], XForm[x3, y3]]}; drawArea: PROCEDURE = {}; TRUSTED{font.GetCharOutline[char, moveTo, lineTo, curveTo, drawArea]}; }; charInfo: PressFontReader.CharInfo _ Info[]; Info: PROC RETURNS [PressFontReader.CharInfo] = TRUSTED {RETURN[font.GetCharInfo[char]]}; visibility: Visibility; loadInfo: REF CharLoadInfo; loadInfo _ NARROW[fontCache.GetCharData[fontCode, char ! XFontCache.CacheMiss => {Load[char]; RETRY}]]; visibility _ BoxVisibility[context, context.transform.f+loadInfo.sMin, loadInfo.sMax-loadInfo.sMin, context.transform.e+loadInfo.fMin, loadInfo.fMax-loadInfo.fMin]; SELECT visibility FROM all => { sMin: CARDINAL ~ Real.RoundLI[context.transform.f] + loadInfo.sMin; fMin: CARDINAL ~ Real.RoundLI[context.transform.e] + loadInfo.fMin; IF loadInfo.sMin < loadInfo.sMax AND loadInfo.fMin < loadInfo.fMax THEN SELECT loadInfo.representation FROM raster => { context.pdState.MaskSamplesRef[loadInfo.loadRef, sMin, fMin]; stats.rasterChars _ stats.rasterChars + 1; }; runGroup => { context.pdState.MaskRunGroupRef[loadInfo.loadRef, sMin, fMin]; stats.runGroupChars _ stats.runGroupChars + 1; }; ENDCASE => ERROR; }; part => { smin: REAL ~ MIN[MAX[context.transform.f + loadInfo.sMin, 0], LAST[NAT]]; smax: REAL ~ MIN[MAX[context.transform.f + loadInfo.sMax, 0], LAST[NAT]]; DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { f: CARDINAL ~ x; FOR s: INTEGER IN [y..y+h) DO SendRun[sMin: s, fMin: f, fSize: w]; ENDLOOP; }; context.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: Real.RoundLI[smin], ymax: Real.RoundLI[smax], parityFill: TRUE]; }; context.devicePath.PushPath[GenPath]; context.pdState.MaskRunGroup[DeliverRuns]; context.devicePath.PopPath; stats.clippedChars _ stats.clippedChars + 1; }; none => {stats.culledChars _ stats.culledChars + 1}; ENDCASE => ERROR; Translate[context, [charInfo.widthX, charInfo.widthY]]; }; map[DoChar]; }; DrawRope: PUBLIC PROC [context: Context, font: PressFontReader.Font, rope: Rope.ROPE] = { Map: PROC [Char: PROC[CHAR]] = { Action: PROC [c: CHAR] RETURNS [quit: BOOL _ FALSE] = {Char[c]}; [] _ rope.Map[action: Action]; }; DrawChars[context, font, Map]; }; DrawChar: PUBLIC PROC [context: Context, font: PressFontReader.Font, char: CHAR] = { MapOneChar: PROC [Char: PROC[CHAR]] = {Char[char]}; DrawChars[context, font, MapOneChar]; }; END. ØPDGraphicsImpl.mesa Michael Plass, May 12, 1983 10:45 am Won't work for 4-color printers. This code is buggy: avoidTrapezoids: BOOLEAN _ TRUE; DrawConvexQuadrilateral: PUBLIC PROC [context: Context, p1, p2, p3, p4: Pair] = { RunGroupQuadrilateral: PROC [p1, p2, p3, p4: Pair] = { GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { move[p1]; line[p2]; line[p3]; line[p4]; }; DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { FOR s: INTEGER IN [y..y+h) DO SendRun[sMin: s, fMin: x, fSize: w]; ENDLOOP; }; context.devicePath.ScanConvert[BoxFromScanConverter]; }; context.devicePath.PushPath[GenPath]; context.pdState.MaskRunGroup[DeliverRuns]; context.devicePath.PopPath; }; Intersect: PROC [m1, m2: Pair, p: Pair] RETURNS [q: Pair] = { deltay: REAL ~ m2.y - m1.y; deltax: REAL ~ m2.x - m1.x; q _ p; IF deltay # 0 THEN q.x _ m1.x + deltax*(p.y - m1.y)/deltay; }; DrawTrap: PROC [fmin1, fmin2, fmax1, fmax2: Pair] = { smin: INT ~ Real.RoundLI[fmin1.y]; fminAtsmin: INT ~ Real.RoundLI[fmin1.x]; fmaxAtsmin: INT ~ Real.RoundLI[fmin2.x]; smax: INT ~ Real.RoundLI[fmax1.y]; fminAtsmax: INT ~ Real.RoundLI[fmax1.x]; fmaxAtsmax: INT ~ Real.RoundLI[fmax2.x]; IF smin >= smax THEN RETURN; IF fminAtsmin = fmaxAtsmin AND fminAtsmax = fmaxAtsmax THEN RETURN; IF fminAtsmin = fminAtsmax AND fmaxAtsmin = fmaxAtsmax THEN { context.pdState.MaskRectangle[sMin: smin, fMin: fminAtsmin, sSize: smax - smin, fSize: fmaxAtsmin - fminAtsmin]; } ELSE IF avoidTrapezoids THEN RunGroupQuadrilateral[fmin1, fmin2, fmax2, fmax1] ELSE context.pdState.MaskTrapezoid[sMin: smin, sSize: smax-smin, fMin: fminAtsmin, fSize: fmaxAtsmin-fminAtsmin, fMinLast: fminAtsmax, fSizeLast: fmaxAtsmax-fminAtsmax]; }; p1 _ TransformPoint[context, p1]; p2 _ TransformPoint[context, p2]; p3 _ TransformPoint[context, p3]; p4 _ TransformPoint[context, p4]; IF p1.y > p2.y THEN {t: Pair _ p1; p1 _ p2; p2 _ t}; IF p1.y > p3.y THEN {t: Pair _ p1; p1 _ p3; p3 _ t}; IF p1.y > p4.y THEN {t: Pair _ p1; p1 _ p4; p4 _ t}; IF p2.y > p3.y THEN {t: Pair _ p2; p2 _ p3; p3 _ t}; IF p2.y > p4.y THEN {t: Pair _ p2; p2 _ p4; p4 _ t}; IF p3.y > p4.y THEN {t: Pair _ p3; p3 _ p4; p4 _ t}; IF context.clippers > 0 OR MIN[p1.x, p2.x, p3.x, p4.x] < 0 OR MAX[p1.x, p2.x, p3.x, p4.x] > context.pdState.GetBounds.fMax OR MIN[p1.y, p2.y, p3.y, p4.y] < 0 OR MAX[p1.y, p2.y, p3.y, p4.y] > context.pdState.GetBounds.sMax THEN { RunGroupQuadrilateral[p1, p2, p4, p3]; } ELSE { q2: Pair _ Intersect[p1, p3, p2]; q3: Pair _ Intersect[p2, p4, p3]; IF q2.x > p2.x THEN {t: Pair _ q2; q2 _ p2; p2 _ t}; IF q3.x > p3.x THEN {t: Pair _ q3; q3 _ p3; p3 _ t}; DrawTrap[p1, p1, q2, p2]; DrawTrap[q2, p2, q3, p3]; DrawTrap[q3, p3, p4, p4]; }; }; ÊɘJšœ™J™$šÏk ˜ J˜ Jšœ ˜ Jšœ˜Jšœ˜Jšœ ˜ J˜J˜J˜J˜ J˜J˜—šœœ˜JšœG˜NJšœ ˜—Jšœ˜J˜Jšœœœ˜ Jšœ œ˜J˜Jšœœ˜ Jš œ œœ œœœ œ˜@Jšœœ˜!Jšœ œ˜#Jšœ œ˜'Jšœ œ˜%šÏn œœœ˜AJšœœ˜šœœœ ˜Jšœ/˜/Jšœ˜—šœ˜Jšœ˜Jšœ œœ˜)Jšœ œœ˜Jšœ ˜ Jšœ˜—Jšœ˜—šž œœœ˜CJšœœ˜šœœœ ˜Jšœ/˜/Jšœ˜—šœ˜Jšœ˜Jšœ œœ˜)Jšœ œœ˜Jšœ ˜ Jšœ˜—Jšœ˜—šž œœœ˜BJšœœ˜šœœœ ˜Jšœ/˜/Jšœ˜—šœ˜Jšœ˜Jšœ œœ˜)Jšœœ˜Jšœ ˜ Jšœ˜—Jšœ˜—Jšœœ œ˜+š žœœœœ œ˜:šœ œ ˜Jšœ$˜$Jšœ˜—Jšœ˜—Jšœ1œ˜Ršžœœ5˜CJšœ˜šœœ˜$Jšœ˜Jšœ ˜ Jšœ˜—Jšœ œœ˜Jšœ*˜*Jšœ˜Jšœ|˜|Jšœ2˜2Jšœ˜—šžœœœœ˜7Jšœœœ ˜8šžœœ˜)Jšœ8œ!˜_JšœF˜FJšœ˜—Jšœ1˜1Jšœ˜—Jšžœœœœœœœœ˜Iš žœœ œœ œ˜:Jšœœ˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœ œ˜J˜ JšœÏc ˜$JšœŸ˜!Jšœœ ˜J˜J˜ J˜$šœ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ˜—J˜—šžœœœ,œ˜JJ™ Jšœ œ˜šžœœ˜)šœœœ˜Jšœ˜J˜ J˜ J˜ Jšœœ˜—Jšœœœ˜5Jšœ8œ!˜_JšœF˜FJšœ˜—Jšœ-˜-Jšœ1˜1Jšœ˜—šžœœœ˜,Jšžœœ7˜DJšœ(˜(Jšœ˜—šžœœœ˜,Jšžœœ9˜FJšœ(˜(Jšœ˜—šž œœœœ˜JJšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜Jš œœ œœœœ˜KJšœ œ˜š œœ œœœœ˜UJ˜5J˜5J˜5J˜4J˜—Jšœœœ*˜GJšœ œœ˜Jšœ*˜*šœœœ ˜šœœœ˜*Jšœ œœœ˜3Jšœ˜—Jšœ˜—Jšœ˜Jšœ†˜†Jšœ3˜3Jšœ˜—š žœœœœœ˜Rš ž œœœœœ˜[Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ œ˜%Jšœ.˜.Jšœ5˜5Jšœý˜ýJšœ'˜'Jšœ+˜+Jšœœœœ œ œ4œœ˜}Jšœ(˜(Jšœ˜—š žœœœœœ˜Rš ž œœœœœ˜[Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ œ˜%Jšœ8˜8Jšœ˜Jšœ.˜.Jšœý˜ýJšœ'˜'Jšœ+˜+Jšœœœœ œ œ4œœ˜}Jšœ(˜(Jšœ˜—šžœœœœ˜RJšœW˜WJšœW˜WJšœ˜—šžœœœœ˜SJšœA˜AJšœA˜AJšœ˜—šžœœœ-˜@Jšœ˜JšœN˜NJšœN˜NJšœN˜NJšœN˜NJšœd˜dJšœd˜dJšœ˜Jšœ˜—Jšž œœœC˜YJšžœœœœ$˜QJš žœœœœœœB˜¥Jš žœœœœœ9˜‚Jš žœœœœœ9˜‚šžœœœ,œ˜IJšœœ=˜PJšœœ=˜PJšœœ9˜LJšœ˜—šžœœœœ œœœ˜EJšœœœ œ˜šœœ˜Jšœ œœ˜!Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ˜ Jšœ˜—šžœœœ˜,šž œœ œœ˜Cšžœœœ˜4šœœœ ˜Jšœ$˜$Jš˜—Jšœ˜—Jšœj˜jJšœ˜—Jšœœ˜Jšœ*˜*Jšœ˜Jšœ˜—šžœœœœ˜>Jšœœœ"˜/š žœœœœœ˜Ušœœ˜šœ˜#Jšœ˜Jšœ˜šœ ˜ JšœH˜HJšœ˜Jšœ˜—Jšœœ˜—Jšœ˜Jšœ˜—Jšœ˜—Jšœœ˜Jšœ.˜.Jšœ&˜&Jšœ˜—šž œœœ˜.Jšœ˜Jšœ&˜&Jšœ˜—Jšœœ˜.Jšœœœ>œ&˜‡JšœœUœ˜kšžœœœUœ˜pJšœ=˜=Jšœ7˜7Jšœ7˜7Jšœ=˜=Jšœ:˜:Jšœ7˜7Jšœ˜—Jšœ œ˜%Jšžœœœœœœœœ˜ZJšžœœœœœœœœ˜\šž œœ.œœ˜_Jšœœ˜Jšœ1˜1Jš œœœœœœ˜dšœœœ˜%Jš œ œœ œœ˜]Jšœ˜ Jšœ˜—šœ˜Jšœ˜Jšœœ˜Jšœœ˜$Jšœœ˜Jšœœ˜$Jšœ œ˜š žœœœœœ˜UJšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—šž œœœ˜,Jšœ œœ ˜*Jšœ œ ˜Jšœ˜Jšœ˜—Jšœ%˜%JšœK˜KJšœ˜Jšœ œ ˜Jšœœ œ ˜"Jšœ˜ J˜—Jšœ˜—Jšœœ˜&š ž œœœ5œœœ˜`Jšœ4˜4Jšœ œ˜$Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšžœœœœœœœ ˜]JšœœŸ˜5JšœF˜Fšžœœœ˜šžœœœœ˜.Jšœ˜Jšœ˜Jšœ˜—š žœœœœœ˜UJšœ œœ˜5Jšœ œœ˜5Jšœ œœ9˜iJšœ œ˜Jšœ?˜FJ˜—šžœœœ˜&Jšœœ˜,Jšœœ˜,Jšœœ˜0Jšœœ˜0J˜Jšœ˜—šž œœ œœ˜Cšžœœœ˜4Jšœœ˜šœœœ&˜9Jšœ$˜$Jš˜—Jšœ˜—JšœKœ˜QJšœ˜—Jšœ œœ˜/Jšœœ˜Jšœœœ ˜8Jšœ˜Jšœ œœ˜.Jšœ œœ˜/Jšœ1œ˜7JšœœC˜fš œ:œ0œ œ*œ˜ÄJšœ=˜=Jšœ#˜#Jšœ.˜.J˜—šœ˜Jš œ œœ!œ œ˜IJšœœ˜š ž œœž œœœœ˜=šžœœœ˜4Jšœœ˜Jšœœœ˜šœ ˜Jšœ˜!Jšœ œ˜Jšœ˜Jšœ˜—šœ$œ&˜QJšœ˜Jšœ˜ —Jšœ˜—JšœKœ˜Qšœ˜Jšœ˜!Jšœ œ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ{˜{Jšœ!˜!J˜*Jšœ˜—Jšœ˜Jšœ1˜1Jšœ˜—šžœœœ˜Jšœœ˜Jšœœ˜šžœœœœ˜.Jšœ˜Jšœ˜Jšœ˜—š žœœœœœ˜UJšœ œœ˜5Jšœ œœ˜5Jšœ œœ9˜iJšœ œ˜Jšœ?˜FJ˜—Jšœ,˜,Jš žœœœœœ˜YJ˜Jšœ œ˜Jšœ œMœ˜gJšœ¤˜¤Jšœ ˜˜Jšœœ5˜CJšœœ5˜Cšœœ˜Gšœ˜#šœ ˜ Jšœ=˜=J˜*J˜—šœ ˜ Jšœ>˜>J˜.J˜—Jšœœ˜——J˜—˜ Jš œœœœ*œœ˜IJš œœœœ*œœ˜Išž œœ œœ˜Cšžœœœ˜4Jšœœ˜šœœœ ˜Jšœ$˜$Jš˜—Jšœ˜—Jšœ{œ˜Jšœ˜—Jšœ%˜%Jšœ*˜*Jšœ˜J˜,J˜—Jšœ4˜4Jšœœ˜Jšœ7˜7Jšœ˜—Jšœ ˜ Jšœ˜—šžœœœ;œ˜Yšžœœœœ˜ Jš œœœœœœ˜@Jšœ˜J˜—Jšœ˜Jšœ˜—šžœ œ6œ˜TJšž œœœœ˜3Jšœ%˜%Jšœ˜—J˜Jšœ˜J˜™Jšœœœ™ šžœœœ-™Qšžœœ™6š žœœœœœ™UJšœ ™ Jšœ ™ J™ J™ Jšœ™—šž œœ œœ™Cšžœœœ™4šœœœ ™Jšœ$™$Jš™—Jšœ™—Jšœ5™5Jšœ™—Jšœ%™%Jšœ*™*Jšœ™Jšœ™—šž œœœ™=Jšœœ™Jšœœ™Jšœ™Jšœ œ)™;Jšœ™—šžœœ'™5Jšœœ™"Jšœ œ™(Jšœ œ™(Jšœœ™"Jšœ œ™(Jšœ œ™(Jšœœœ™Jšœœœœ™Cšœœœ™=Jšœp™pJšœ™—Jšœœœ2™NJšœ¥™©Jšœ™—Jšœ!™!Jšœ!™!Jšœ!™!Jšœ!™!Jšœ œ!™4Jšœ œ!™4Jšœ œ!™4Jšœ œ!™4Jšœ œ!™4Jšœ œ!™4Jšœ™Jšœœ™"Jšœœ9™?Jšœœ™"šœœ:œ™FJšœ&™&Jšœ™—šœ™Jšœ!™!Jšœ!™!Jšœ œ!™4Jšœ œ!™4Jšœ™Jšœ™Jšœ™J™—Jšœ™—J™——…—I0kÑ