DIRECTORY Basics, Font, Imager, ImagerBasic, ImagerBrick, ImagerConic, ImagerDefault, ImagerFontCache, ImagerHalftone, ImagerManhattan, ImagerMasks, ImagerPD, ImagerPDExtras, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, PDFileWriter, Real, RefText, Rope, Scaled ; ImagerPDImpl: CEDAR PROGRAM IMPORTS Imager, ImagerConic, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, PDFileWriter, Real, RefText, Scaled, ImagerDefault, ImagerManhattan EXPORTS ImagerPD, ImagerPDExtras ~ BEGIN OPEN ImagerBasic; PDFileDescription: TYPE ~ ImagerPD.PDFileDescription; PDFileDescriptionRep: TYPE ~ ImagerPD.PDFileDescriptionRep; FONT: TYPE ~ Font.FONT; ROPE: TYPE ~ Font.ROPE; DeviceCode: TYPE ~ PDFileWriter.DeviceCode; Mask: TYPE ~ ImagerMasks.Mask; Toner: TYPE ~ PDFileWriter.Toner; TransformationRec: TYPE ~ ImagerTransform.TransformationRec; PixelMap: TYPE ~ ImagerPixelMaps.PixelMap; Tile: TYPE ~ ImagerPixelMaps.Tile; SpecialColor: TYPE ~ REF ColorRep[special]; Name: TYPE ~ ImagerPrivate.Name; ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon; Context: TYPE ~ Imager.Context; State: TYPE ~ ImagerDefault.State; Raven: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, raven, 300, 300, 2550, 3300, 1, 16, 60000, TRUE, 1 ]]] }; Hornet: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, hornet, 384, 384, 3264, 4224, 1, 16, 60000, TRUE, 1 ]]] }; Gnat: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, gnat, 384, 384, 4224, 5376, 1, 16, 60000, TRUE, 1 ]]] }; PlateMaker: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, mig, 880, 880, 9680, 7480, 1, 16, 60000, TRUE, 1 ]]] }; ReticleMaker: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, reticleMaker, 880, 880, 8800, 8800, 1, 16, 60000, TRUE, 1 ]]] }; Puffin: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, puffin, 384, 384, 4224, 3264, 3, 16, 60000, TRUE, 1 ]]] }; ColorVersatec: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, VAL[7], 200, 200, 20000, 8000, 4, 64, 100000, FALSE, 1 ]]] }; StatsRecord: TYPE = RECORD[ loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT _ 0]; stats: StatsRecord _ []; Stats: PROC RETURNS[StatsRecord] = { x: StatsRecord = stats; stats _ []; RETURN[x] }; 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]; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ viewClipper: ManhattanPolygon, -- in device coords clientClipper: ClientClipper, -- for noticing client clipper changes compositeClipper: ManhattanPolygon, -- in device coords; invalid if NIL or clientClipper#state.clipper xScale, yScale: REAL, viewOrigin: IntPair, surfaceToDevice: Transformation, surfaceBounds: DeviceRectangle, devicePath: ImagerScanConverter.DevicePath, cachedColor: Color _ NIL, grayTileRef: ARRAY Toner OF ARRAY [0..64) OF PDFileWriter.LoadReference, stipples: LIST OF StippleRep _ NIL, fontCache: ImagerFontCache.FontCache, pdState: PDFileWriter.PDState, leftoverMode: BOOLEAN, pdStatePriorityImportant: INT _ 0 ]; StippleRep: TYPE ~ RECORD [ loadReference: PDFileWriter.LoadReference, stipple: PixelMap ]; Round: PROC [r: REAL] RETURNS [INTEGER] ~ { IF r > LAST[INTEGER] THEN RETURN [LAST[INTEGER]]; IF r < FIRST[INTEGER] THEN RETURN [FIRST[INTEGER]]; RETURN [Real.RoundI[r]]; }; CompositeT: PROC [data: Data, state: State] RETURNS [Transformation] ~ { t: TransformationRec _ state.T.Contents; t.c _ t.c + data.viewOrigin.x; t.f _ t.f + data.viewOrigin.y; RETURN [ImagerTransform.Concat[t.FromRec, data.surfaceToDevice]]; }; Init: PROC [context: Context, info: REF] ~ { data: Data ~ NEW[DataRep]; desc: PDFileDescription ~ NARROW[info]; sScale: REAL ~ desc.sResolution/0.0254; fScale: REAL ~ desc.fResolution/0.0254; context.data _ data; data.surfaceBounds _ [sMin: 0, fMin: 0, sSize: desc.imageSSize, fSize: desc.imageFSize]; IF desc.imageFSize >= desc.imageSSize THEN { data.surfaceToDevice _ ImagerTransform.Create[1, 0, 0, 0, 1, 0]; data.xScale _ sScale; data.yScale _ fScale; } ELSE { data.surfaceToDevice _ ImagerTransform.Create[0, -1, desc.imageSSize, 1, 0, 0]; data.xScale _ fScale; data.yScale _ sScale; }; SetViewOrigin[context, [0, 0]]; SetViewBox[context, GetSurfaceBounds[context]]; data.pdState _ PDFileWriter.Create[fileName: desc.fileName, deviceCode: desc.deviceCode, sResolution: desc.sResolution, fResolution: desc.fResolution, imageSSize: desc.imageSSize, imageFSize: desc.imageFSize, bandSSize: desc.bandSSize, copies: desc.copies, leftOverMode: desc.leftovers]; data.leftoverMode _ desc.leftovers; data.fontCache _ ImagerFontCache.Create[]; FOR toner: Toner IN Toner DO FOR i: [0..64) IN [0..64) DO data.grayTileRef[toner][i] _ dummyLoadReference ENDLOOP ENDLOOP; ImagerDefault.InitState[context]; Reset[context]; }; Reset: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; state: State ~ NARROW[context.state]; surfaceSize: Pair ~ ImagerTransform.InverseTransformVec[ [data.surfaceBounds.sSize, data.surfaceBounds.fSize], data.surfaceToDevice ]; ImagerDefault.Reset[context]; state.T _ ImagerTransform.Scale2[data.xScale, data.yScale]; state.fieldXMax _ state.mediumXSize _ surfaceSize.x/data.xScale; state.fieldYMax _ state.mediumYSize _ surfaceSize.y/data.yScale; data.compositeClipper _ NIL; data.cachedColor _ NIL; }; DevicePathFromViewPath: PROC [context: Context, pathMap: PathMapType, pathData: REF] RETURNS [ImagerScanConverter.DevicePath] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; GenPath: ImagerScanConverter.PathProc ~ { Xform: PROC [p: Pair] RETURNS [Pair] ~ { p.y _ p.y + data.viewOrigin.y; p.x _ p.x + data.viewOrigin.x; RETURN[ImagerTransform.Transform[p, data.surfaceToDevice]]; }; Xmove: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; move[q.x, q.y]; lp _ q }; Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp _ q }; Xcurve: PROC [p1, p2, p3: Pair] ~ { q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; q3: Pair ~ Xform[p3]; curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y]; lp _ q3 }; Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]}; Xconic: PROC [p1, p2: Pair, r: REAL] ~ { q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; ImagerConic.ToCurves[lp, q1, q2, r, Curve]; lp _ q2; }; lp: Pair; pathMap[pathData, Xmove, Xline, Xcurve, Xconic]; }; RETURN [ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: data.surfaceBounds]]; }; DevicePathFromPath: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; GenPath: ImagerScanConverter.PathProc ~ { m: TransformationRec ~ CompositeT[data, state].Contents; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ m.a * p.x + m.b * p.y + m.c, m.d * p.x + m.e * p.y + m.f ]]}; Xmove: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; move[q.x, q.y]; lp _ q }; Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp _ q }; Xcurve: PROC [p1, p2, p3: Pair] ~ { q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; q3: Pair ~ Xform[p3]; curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y]; lp _ q3 }; Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]}; Xconic: PROC [p1, p2: Pair, r: REAL] ~ { q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; ImagerConic.ToCurves[lp, q1, q2, r, Curve]; lp _ q2; }; lp: Pair; pathMap[pathData, Xmove, Xline, Xcurve, Xconic]; }; data.devicePath _ ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: data.surfaceBounds, scratch: data.devicePath]; }; DevicePathFromStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL, strokeEnd: StrokeEnd, closed: BOOL] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; data.devicePath _ ImagerStroke.DevicePathFromStroke[ pathMap: pathMap, pathData: pathData, clientToDevice: CompositeT[data, state], width: IF LOOPHOLE[strokeWidth, LONG CARDINAL] # LOOPHOLE[Imager.defaultStrokeWidth, LONG CARDINAL] THEN strokeWidth ELSE state.strokeWidth, strokeEnd: IF strokeEnd # nil THEN strokeEnd ELSE SELECT state.strokeEnd FROM 0 => square, 1 => butt, 2 => round, ENDCASE => nil, closed: closed, clipBox: data.surfaceBounds, scratch: data.devicePath ]; }; CompositeClipper: PROC [context: Context] RETURNS [ManhattanPolygon] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; mp: ManhattanPolygon _ data.compositeClipper; IF mp = NIL OR data.clientClipper # state.clipper THEN { ConcatClippers: PROC [l: LIST OF ClientClipperItem] ~ { IF l#NIL THEN { t1, t2: ManhattanPolygon; ConcatClippers[l.rest]; t1 _ ImagerScanConverter.ConvertToManhattanPolygon[DevicePathFromViewPath[context: context, pathMap: l.first.pathMap, pathData: l.first.pathData], data.surfaceBounds]; t2 _ mp; mp _ IF l.first.exclude THEN mp.Difference[t1] ELSE mp.Intersection[t1]; ImagerManhattan.Destroy[t1]; ImagerManhattan.Destroy[t2]; }; }; ImagerManhattan.Destroy[data.compositeClipper]; data.clientClipper _ state.clipper; mp _ ImagerManhattan.Copy[data.viewClipper]; ConcatClippers[data.clientClipper]; data.compositeClipper _ mp; }; RETURN [mp] }; MakeTileSamples: PROC [xSize, ySize: NAT, sample: NAT] RETURNS [tileSamples: PixelArray] ~ { tileSamples _ NEW[PixelArrayRep]; tileSamples.m _ ImagerTransform.Rotate[0]; tileSamples.xPixels _ xSize; tileSamples.yPixels _ ySize; tileSamples.maxSampleValue _ 255; tileSamples.samplesPerPixel _ 1; tileSamples.get _ ConstantGet; tileSamples.data _ NEW[NAT _ sample]; }; ConstantGet: PROC [self: PixelArray, buffer: PixelBuffer, nSamples: NAT, layer: INT, xStart, yStart: Scaled.Value, xDelta, yDelta: Scaled.Value] ~ { WITH self.data SELECT FROM n: REF NAT => { value: NAT _ n^; FOR i: NAT IN [0..nSamples) DO buffer[i] _ value ENDLOOP; }; ENDCASE => ERROR; }; deviceBrickDefault: ImagerHalftone.DeviceBrick _ ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, 255]; ConstructTile: PROC [data: Data, toner: Toner, index: [0..64), customBrick: ImagerBrick.Brick _ NIL] ~ { deviceBrick: ImagerHalftone.DeviceBrick _ IF customBrick # NIL THEN ImagerHalftone.MakeDeviceBrick[customBrick, 255] ELSE ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, 255]; pixelMap: ImagerPixelMaps.PixelMap _ ImagerPixelMaps.Create[0, [0, 0, deviceBrick.sPeriod, deviceBrick.fPeriod]]; loadReference: PDFileWriter.LoadReference; Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ { FOR s: INTEGER IN [0..deviceBrick.sPeriod) DO run[s, 0, deviceBrick.fPeriod]; ENDLOOP; }; ImagerHalftone.Halftone[ dest: pixelMap, runs: Runs, source: MakeTileSamples[16, 16, trc[index]], transformation: ImagerTransform.Rotate[0], deviceBrick: deviceBrick ]; loadReference _ data.pdState.LoadContiguousColorTile[phase: deviceBrick.phase, sMin: 0, fMin: 0, sSize: deviceBrick.sPeriod, fSize: deviceBrick.fPeriod, bitsPtr: pixelMap.refRep.pointer]; data.grayTileRef[toner][index] _ loadReference; }; CurrentColor: PROC [context: Context] RETURNS [color: Color] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; SetConstantColor: PROC [intensity: REAL] ~ { index: [0..64) _ Real.RoundI[MAX[MIN[intensity*63, 63], 0]]; DoToner: PROC [toner: Toner] = { IF data.grayTileRef[toner][index] = dummyLoadReference THEN ConstructTile[data, toner, index, NARROW[Imager.GetProp[context, $CustomBrick]]]; data.pdState.SetColorTile[toner, data.grayTileRef[toner][index]] }; data.pdState.DoForEachToner[DoToner]; }; IF data.pdStatePriorityImportant # state.priorityImportant THEN { [] _ data.pdState.SetPriorityImportant[state.priorityImportant#0]; data.pdStatePriorityImportant _ state.priorityImportant; }; IF data.cachedColor = state.color THEN RETURN [data.cachedColor]; color _ data.cachedColor _ state.color; IF color = Imager.black THEN { DoToner: PROC [toner: Toner] ~ {data.pdState.SetColorInk[toner]}; data.pdState.DoForEachToner[DoToner]; } ELSE IF color = Imager.white THEN { DoToner: PROC [toner: Toner] ~ {data.pdState.SetColorClear[toner]}; data.pdState.DoForEachToner[DoToner]; } ELSE WITH color SELECT FROM constantColor: ConstantColor => { SetConstantColor[constantColor.Y/65536.0]; }; specialColor: SpecialColor => { WITH specialColor.ref SELECT FROM stipple: REF CARDINAL => { oneBits: NAT _ 0; FOR v: CARDINAL _ stipple^, v/2 UNTIL v=0 DO oneBits _ oneBits + v MOD 2 ENDLOOP; SetConstantColor[(16-oneBits)/16.0]; }; ENDCASE => ERROR Imager.Error[$UnknownSpecialColor]; }; sampledColor: SampledColor => { IF sampledColor.colorOperator = $SampledBlack THEN { transform: Transformation _ ImagerTransform.Concat[ImagerTransform.Concat[ImagerTransform.Concat[ sampledColor.pa.m, sampledColor.m], ImagerTransform.Translate[data.viewOrigin.x, data.viewOrigin.y]], data.surfaceToDevice]; tileRect: Rectangle _ ImagerTransform.TransformRectangle[[0, 0, sampledColor.pa.xPixels, sampledColor.pa.yPixels], transform]; transformRec: TransformationRec _ ImagerTransform.Contents[transform]; easyTransform: BOOLEAN ~ (transformRec.a = 0 AND transformRec.e = 0) OR (transformRec.b = 0 AND transformRec.d = 0); tileArea: REAL _ tileRect.w*tileRect.h; IF easyTransform AND IsInteger[tileRect.w] AND IsInteger[tileRect.h] AND tileArea < 4096 AND data.pdState.RemainingLoadSize > Real.RoundLI[tileArea/16] + 3500 THEN { mask: Mask _ ImagerMasks.FromPixelArray[sampledColor.pa, transform]; bb: DeviceRectangle _ ImagerMasks.BoundingBox[mask]; pixels: PixelMap _ ImagerMasks.ToPixelMap[mask, ImagerMasks.FromRectangle[bb]]; tFlag: PDFileWriter.TFlag _ IF sampledColor.transparent THEN transparent ELSE opaque; color _ Imager.black; FOR s: LIST OF StippleRep _ data.stipples, s.rest UNTIL s = NIL DO IF s.first.stipple.Equal[pixels] THEN { data.pdState.SetColorTile[black, s.first.loadReference, tFlag]; pixels.refRep.pointer _ NIL; pixels.refRep.ref _ NIL; pixels.refRep _ NIL; RETURN; }; ENDLOOP; data.stipples _ CONS[[ loadReference: data.pdState.LoadContiguousColorTile[ phase: 0, sMin: pixels.sMin+pixels.sOrigin, fMin: pixels.fMin+pixels.fOrigin, sSize: pixels.sSize, fSize: pixels.fSize, bitsPtr: pixels.refRep.pointer ], stipple: pixels], data.stipples]; data.pdState.SetColorTile[black, data.stipples.first.loadReference, tFlag]; RETURN; }; data.pdState.SetColorInk[black]; }; }; ENDCASE => state.color _ Imager.black; }; IsInteger: PROC [r: REAL] RETURNS [BOOLEAN] ~ { IF ABS[r] > 32000 THEN RETURN [FALSE]; RETURN [Real.RoundLI[r] = (r+1000.0)-1000.0]; }; ApplyMask: PROC [context: Context, mask: REF, sTranslate, fTranslate: INTEGER _ 0] ~ { state: State ~ NARROW[context.state]; IF state.noImage = 0 THEN { data: Data ~ NARROW[context.data]; color: Color ~ CurrentColor[context]; clipper: ManhattanPolygon _ CompositeClipper[context]; WITH color SELECT FROM sampledColor: SampledColor => TRUSTED { lineBuffer: ImagerPixelMaps.PixelMap _ ImagerPixelMaps.Create[0, [0, 0, 1, data.surfaceBounds.fSize]]; customBrick: ImagerBrick.Brick _ NARROW[Imager.GetProp[context, $CustomBrick]]; brick: ImagerHalftone.DeviceBrick _ IF customBrick # NIL THEN ImagerHalftone.MakeDeviceBrick[customBrick, sampledColor.pa.maxSampleValue] ELSE ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, sampledColor.pa.maxSampleValue]; transform: Transformation _ ImagerTransform.Concat[ImagerTransform.Concat[ImagerTransform.Concat[ sampledColor.pa.m, sampledColor.m], ImagerTransform.Translate[data.viewOrigin.x, data.viewOrigin.y]], data.surfaceToDevice]; linePointer: LONG POINTER _ lineBuffer.refRep.pointer; Line: PROC [proc: PROC [LONG POINTER]] ~ TRUSTED {proc[linePointer]}; SampledColorRun: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED { Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ TRUSTED { run[sMin, fMin, fSize]; }; lineBuffer.sOrigin _ sMin; lineBuffer.fOrigin _ fMin; lineBuffer.fSize _ fSize; ImagerHalftone.Halftone[lineBuffer, Runs, sampledColor.pa, transform, brick, invertOutput]; PDFileWriter.ColorSamples[data.pdState, black, sMin, fMin, 1, fSize, Line, IF sampledColor.transparent THEN transparent ELSE opaque]; }; invertOutput: BOOLEAN _ FALSE; SELECT sampledColor.colorOperator FROM $SampledBlack => invertOutput _ TRUE; $Intensity => NULL; ENDCASE => Imager.Error[$UnknownColorModel]; ImagerMasks.GenerateRuns[mask, clipper, SampledColorRun, sTranslate, fTranslate]; }; ENDCASE => { DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] ~ { Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { SendRun[sMin, fMin, fSize]; }; ImagerMasks.GenerateRuns[mask, clipper, Run, sTranslate, fTranslate]; }; PDFileWriter.MaskRunGroup[data.pdState, DeliverRuns]; }; }; }; MaskStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL, strokeEnd: StrokeEnd] ~ { data: Data ~ NARROW[context.data]; DevicePathFromStroke[context, pathMap, pathData, strokeWidth, strokeEnd, FALSE]; ApplyMask[context, data.devicePath]; }; MaskStrokeClosed: PROC [context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL] = { data: Data ~ NARROW[context.data]; DevicePathFromStroke[context, pathMap, pathData, strokeWidth, nil, TRUE]; ApplyMask[context, data.devicePath]; }; MaskFill: PROC[context: Context, pathMap: PathMapType, pathData: REF] ~ { data: Data ~ NARROW[context.data]; DevicePathFromPath[context, pathMap, pathData]; ApplyMask[context, data.devicePath]; }; MaskPixel: PROC [context: Context, pa: PixelArray] ~ { data: Data _ NARROW[context.data]; state: State ~ NARROW[context.state]; trans: Transformation ~ CompositeT[data, state]; mask: REF _ ImagerMasks.FromPixelArray[pa, ImagerTransform.Concat[pa.m, trans]]; ApplyMask[context, mask]; mask _ NIL; }; specialCaseRectangles: BOOLEAN _ TRUE; MaskRectangle: PROC [context: Context, x, y, w, h: REAL] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; compositeT: Transformation ~ CompositeT[data, state]; IF specialCaseRectangles AND Easy[compositeT] THEN { t1: ManhattanPolygon _ ManhattanPolygonFromEasyBox[compositeT, x, y, w, h]; color: Color ~ CurrentColor[context]; clipped: ManhattanPolygon _ t1.Intersection[CompositeClipper[context]]; IF clipped = NIL THEN NULL ELSE IF clipped.rest = NIL AND color.tag = constant THEN { PDFileWriter.MaskRectangle[data.pdState, clipped.first.sMin, clipped.first.fMin, clipped.first.sSize, clipped.first.fSize]; } ELSE ApplyMask[context, t1, 0, 0]; ImagerManhattan.Destroy[t1]; ImagerManhattan.Destroy[clipped]; } ELSE { PathMap: PathMapType ~ { move[[x, y]]; line[[x+w, y]]; line[[x+w, y+h]]; line[[x, y+h]]; }; MaskFill[context, PathMap, NIL]; }; }; IntegerMaskRectangle: PROC[context: Context, x, y, w, h: INTEGER] = { MaskRectangle[context, x, y, w, h]; }; rasterToRunGroupStorageRatio: INT _ 1; CharRepresentation: TYPE ~ {null, runGroup, raster}; CharLoadInfo: TYPE ~ RECORD[ sWidth, fWidth: Scaled.Value, loadRef: PDFileWriter.LoadReference, representation: CharRepresentation, mask: Mask, loadRepSize: INT ]; GetCharMask: PROC [font: FONT, transformation: Transformation, char: CHAR] RETURNS [ImagerManhattan.Polygon] ~ { Runs: PROC [ run: PROC [sMin, fMin: INTEGER, fSize: NAT], repeat: PROC [timesToRepeatScanline: NAT]] ~ { Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { run[sMin: sMin, fMin: fMin, fSize: fSize]; }; font.fontGraphicsClass.maskProc[font, transformation, char, Run]; }; RETURN [ImagerManhattan.CreateFromRuns[Runs]] }; FontCompositeTransform: PROC [context: Context, font: FONT] RETURNS [t: Transformation] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; r: TransformationRec _ CompositeT[data, state].Contents; r.c _ r.f _ 0; t _ ImagerTransform.Concat[font.actualTransformation, r.FromRec]; }; bigRectangle: LIST OF DeviceRectangle ~ LIST[[0, 0, LAST[NAT]/2, LAST[NAT]/2]]; LoadRunGroup: PROC [pdState: PDFileWriter.PDState, mask: Mask] RETURNS [loadReference: PDFileWriter.LoadReference] ~ { DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = { Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {SendRun[sMin, fMin, fSize]}; ImagerMasks.GenerateRuns[mask, bigRectangle, Run, -bb.sMin, -bb.fMin]; }; bb: DeviceRectangle _ ImagerMasks.BoundingBox[mask]; loadReference _ pdState.LoadRunGroup[DeliverRuns]; stats.loadRunGroups _ stats.loadRunGroups + 1; }; LoadBitmap: PROC [pdState: PDFileWriter.PDState, mask: Mask] RETURNS [loadReference: PDFileWriter.LoadReference] ~ { WITH mask SELECT FROM b: REF ImagerPixelMaps.PixelMap => { loadReference _ pdState.LoadContiguousSampleArray[sSize: b^.sSize, fSize: b^.fSize, bitsPtr: b^.refRep.pointer]; }; ENDCASE => ERROR; stats.loadRasters _ stats.loadRasters + 1; }; LoadCharData: PROC[self: ImagerFontCache.FontObject, charCode: CARDINAL] RETURNS [charData: REF, memoryCost: INT] ~ { font: FONT ~ NARROW[self.fontAnyData]; char: CHAR ~ VAL[charCode]; transform: Transformation ~ ImagerTransform.Create[self.r0, self.r1, self.r2, self.r3, self.r4, self.r5]; -- character (to client) to device clientTransform: Transformation ~ ImagerTransform.Concat[ ImagerTransform.Invert[font.actualTransformation], transform]; loadInfo: REF CharLoadInfo ~ NEW[CharLoadInfo]; mask: ImagerManhattan.Polygon ~ GetCharMask[font, transform, char]; nRuns: INT ~ ImagerManhattan.CountRuns[mask]; bb: DeviceRectangle ~ ImagerManhattan.BoundingBox[mask]; runGroupSize: INT _ 2 * nRuns; rasterSize: INT _ bb.sSize * INT[(bb.fSize+15)/16] + 2; width: Pair _ ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char], clientTransform]; loadInfo.loadRef _ dummyLoadReference; loadInfo.mask _ mask; SELECT TRUE FROM bb.sSize = 0 OR bb.fSize = 0 => { loadInfo.representation _ null; loadInfo.loadRepSize _ 0; }; bb.fSize > 32*Basics.bitsPerWord OR runGroupSize*rasterToRunGroupStorageRatio < rasterSize => { loadInfo.representation _ runGroup; loadInfo.loadRepSize _ runGroupSize; }; ENDCASE => { loadInfo.representation _ raster; loadInfo.loadRepSize _ rasterSize; loadInfo.mask _ ImagerMasks.FromBitmap[ImagerMasks.ToPixelMap[mask, ImagerMasks.FromRectangle[bb]]]; ImagerManhattan.Destroy[mask]; }; loadInfo.sWidth _ Scaled.FromReal[width.x]; loadInfo.fWidth _ Scaled.FromReal[width.y]; RETURN [loadInfo, 0]; }; ShowChar: PROC [context: Context, char: CHAR, font: FONT] ~ { text: REF TEXT _ RefText.ObtainScratch[1]; text.length _ 1; text[0] _ char; ShowCharacters[context, text, font, 0, 1]; RefText.ReleaseScratch[text]; }; ShowCharacters: PROC [ context: Context, characters: REF, -- may be a ROPE or a REF TEXT font: FONT, start: INT _ 0, length: INT _ LAST[INT] ] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; showVec: FONT _ IF font = NIL THEN NARROW[state.showVec] ELSE font; surfaceToDevice: TransformationRec _ data.surfaceToDevice.Contents; TransformDeviceToViewVec: PROC [s, f: Scaled.Value] RETURNS [v: Pair] ~ { v _ IF surfaceToDevice.a = 0 THEN [x: Scaled.Float[f], y: -Scaled.Float[s]] ELSE [x: Scaled.Float[s], y: Scaled.Float[f]] }; TransformViewToDevice: PROC [v: Pair] RETURNS [Pair] ~ {RETURN [ IF surfaceToDevice.a = 0 THEN [-(v.y+data.viewOrigin.y) + surfaceToDevice.c, (v.x+data.viewOrigin.x) + surfaceToDevice.f] ELSE [(v.x+data.viewOrigin.x) + surfaceToDevice.c, (v.y+data.viewOrigin.y) + surfaceToDevice.f] ]}; clipper: ManhattanPolygon _ CompositeClipper[context]; transform: TransformationRec ~ FontCompositeTransform[context, showVec].Contents; fontCode: ImagerFontCache.FontCode _ ImagerFontCache.GetFontCode[[ CharDataProc: LoadCharData, r0: transform.a, r1: transform.b, r2: transform.c, r3: transform.d, r4: transform.e, r5: transform.f, fontScalarData: 0, -- unused fontAnyData: showVec]]; bbScratch: LIST OF DeviceRectangle _ LIST[[0,0,0,0]]; noImage: BOOLEAN ~ state.noImage # 0; currentColor: Color ~ CurrentColor[context]; DoChar: PROC[charCode: CARDINAL, charData: REF] = { loadInfo: REF CharLoadInfo ~ NARROW[charData]; delta: Pair _ TransformDeviceToViewVec[loadInfo.sWidth, loadInfo.fWidth]; cp: Pair _ TransformViewToDevice[[state.cpx, state.cpy]]; IF NOT noImage AND ABS[cp.x] < 32000.0 AND ABS[cp.y] < 32000.0 THEN { sCP: INTEGER _ Real.RoundI[cp.x]; fCP: INTEGER _ Real.RoundI[cp.y]; visibility: Visibility; bbScratch.first _ ImagerMasks.BoundingBox[loadInfo.mask]; bbScratch.first.sMin _ bbScratch.first.sMin + sCP; bbScratch.first.fMin _ bbScratch.first.fMin + fCP; visibility _ ImagerManhattan.IsVisible[bbScratch, clipper]; IF visibility = visible THEN WITH currentColor SELECT FROM sampled: SampledColor => visibility _ partlyVisible; ENDCASE => NULL; IF loadInfo.loadRef = dummyLoadReference AND visibility = visible THEN { IF data.pdState.RemainingLoadSize > loadInfo.loadRepSize + 3500 THEN { SELECT loadInfo.representation FROM raster => loadInfo.loadRef _ LoadBitmap[data.pdState, loadInfo.mask]; runGroup => loadInfo.loadRef _ LoadRunGroup[data.pdState, loadInfo.mask]; ENDCASE; } ELSE visibility _ partlyVisible; }; SELECT visibility FROM visible => { SELECT loadInfo.representation FROM raster => { IF loadInfo.loadRef = dummyLoadReference THEN ERROR; data.pdState.MaskSamplesRef[loadInfo.loadRef, bbScratch.first.sMin, bbScratch.first.fMin]; stats.rasterChars _ stats.rasterChars + 1; }; runGroup => { IF loadInfo.loadRef = dummyLoadReference THEN ERROR; data.pdState.MaskRunGroupRef[loadInfo.loadRef, bbScratch.first.sMin, bbScratch.first.fMin]; stats.runGroupChars _ stats.runGroupChars + 1; }; null => NULL; ENDCASE => ERROR; }; partlyVisible => { ApplyMask[context, loadInfo.mask, sCP, fCP]; stats.clippedChars _ stats.clippedChars + 1; }; invisible => {stats.culledChars _ stats.culledChars + 1}; ENDCASE => ERROR; }; IF charCode = ORD[' ] THEN { IF state.amplifySpace # 1.0 THEN { delta.x _ delta.x * state.amplifySpace; delta.y _ delta.y * state.amplifySpace; }; ImagerDefault.CorrectSpaceView[context, delta]; } ELSE { ImagerDefault.CorrectMask[context]; }; state.cpx _ state.cpx + delta.x; state.cpy _ state.cpy + delta.y; }; ImagerFontCache.GetStringData[DoChar, data.fontCache, fontCode, characters, start, length]; }; ManhattanPolygonFromSurfaceRectangle: PROC [context: Context, box: IntRectangle] RETURNS [LIST OF DeviceRectangle] ~ { data: Data ~ NARROW[context.data]; deviceBox: IntRectangle _ ImagerTransform.TransformIntRectangle[[x: box.x, y: box.y, w: box.w, h: box.h], data.surfaceToDevice]; RETURN [ImagerManhattan.CreateFromBox[[deviceBox.x, deviceBox.y, deviceBox.w, deviceBox.h]]]; }; SetViewOrigin: PROC [context: Context, viewOrigin: IntPair] ~ { data: Data _ NARROW[context.data]; data.viewOrigin _ viewOrigin; }; GetViewOrigin: PROC [context: Context] RETURNS [viewOrigin: IntPair] ~ { data: Data ~ NARROW[context.data]; RETURN[data.viewOrigin]; }; SetViewBox: PROC [context: Context, viewBox: IntRectangle] ~ { data: Data _ NARROW[context.data]; surfaceBox: IntRectangle _ [ x: viewBox.x+data.viewOrigin.x, y: viewBox.y+data.viewOrigin.y, w: viewBox.w, h: viewBox.h ]; mask: ImagerManhattan.Polygon _ ManhattanPolygonFromSurfaceRectangle[context, surfaceBox]; ImagerManhattan.Destroy[data.compositeClipper]; data.compositeClipper _ NIL; ImagerManhattan.Destroy[data.viewClipper]; data.viewClipper _ mask; }; GetViewBox: PROC [context: Context] RETURNS [IntRectangle] ~ { data: Data ~ NARROW[context.data]; deviceBox: DeviceRectangle _ ImagerManhattan.BoundingBox[data.viewClipper]; deviceToSurface: Transformation _ ImagerTransform.Invert[data.surfaceToDevice]; surfaceBox: IntRectangle _ ImagerTransform.TransformIntRectangle[[deviceBox.sMin, deviceBox.fMin, deviceBox.sSize, deviceBox.fSize], deviceToSurface]; RETURN[[ x: surfaceBox.x-data.viewOrigin.x, y: surfaceBox.y-data.viewOrigin.y, w: surfaceBox.w, h: surfaceBox.h ]]; }; ClipView: PROC [context: Context, clipBox: IntRectangle, exclude: BOOLEAN] ~ { data: Data _ NARROW[context.data]; surfaceBox: IntRectangle _ [ x: clipBox.x+data.viewOrigin.x, y: clipBox.y+data.viewOrigin.y, w: clipBox.w, h: clipBox.h ]; newBox: ImagerManhattan.Polygon _ ManhattanPolygonFromSurfaceRectangle[context, surfaceBox]; old: ImagerManhattan.Polygon _ data.viewClipper; ImagerManhattan.Destroy[data.compositeClipper]; data.compositeClipper _ NIL; data.viewClipper _ IF exclude THEN ImagerManhattan.Difference[old, newBox] ELSE ImagerManhattan.Intersection[old, newBox]; newBox.rest _ old; ImagerManhattan.Destroy[newBox]; }; MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] ~ { ERROR Imager.Error[$NotImplementedForThisDevice]; }; Zero: PROC [real: REAL] RETURNS [BOOLEAN] ~ INLINE { RETURN [LOOPHOLE[real, LONG CARDINAL] = LOOPHOLE[0.0, LONG CARDINAL]] }; Easy: PROC [trans: Transformation] RETURNS [BOOLEAN] ~ { RETURN [(Zero[trans.Contents.b] AND Zero[trans.Contents.d]) OR (Zero[trans.Contents.a] AND Zero[trans.Contents.e])] }; ManhattanPolygonFromEasyBox: PROC [compositeT: Transformation, x, y, w, h: REAL] RETURNS [manhattanPolygon: ImagerManhattan.Polygon] ~ { trans: TransformationRec ~ compositeT.Contents; x1: REAL ~ x+w; y1: REAL ~ y+h; s0: INTEGER ~ Round[trans.a * x + trans.b * y + trans.c]; s1: INTEGER ~ Round[trans.a * x1 + trans.b * y1 + trans.c]; f0: INTEGER ~ Round[trans.d * x + trans.e * y + trans.f]; f1: INTEGER ~ Round[trans.d * x1 + trans.e * y1 + trans.f]; sMin: INTEGER ~ MIN[s0, s1]; sMax: INTEGER ~ MAX[s0, s1]; fMin: INTEGER ~ MIN[f0, f1]; fMax: INTEGER ~ MAX[f0, f1]; manhattanPolygon _ ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]]; }; TestRectangle: PROC [context: Context, x, y, w, h: REAL] RETURNS [visibility: Visibility] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; compositeT: Transformation ~ CompositeT[data, state]; manhattanPolygon: ImagerManhattan.Polygon _ NIL; IF specialCaseRectangles AND Easy[compositeT] THEN manhattanPolygon _ ManhattanPolygonFromEasyBox[compositeT, x, y, w, h] ELSE { PathMap: PathMapType ~ { move[[x, y]]; line[[x+w, y]]; line[[x+w, y+h]]; line[[x, y+h]]; }; DevicePathFromPath[context, PathMap, NIL]; manhattanPolygon _ ImagerScanConverter.ConvertToManhattanPolygon[data.devicePath, data.surfaceBounds]; }; visibility _ ImagerManhattan.IsVisible[manhattanPolygon, CompositeClipper[context]]; ImagerManhattan.Destroy[manhattanPolygon]; }; GetSurfaceBounds: PROC[context: Context] RETURNS[IntRectangle] = { data: Data ~ NARROW[context.data]; b: DeviceRectangle ~ data.surfaceBounds; deviceToSurface: Transformation ~ ImagerTransform.Invert[data.surfaceToDevice]; RETURN [ImagerTransform.TransformIntRectangle[[b.sMin, b.fMin, b.sSize, b.fSize], deviceToSurface]] }; NewPage: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; data.pdState.EndPage; Reset[context]; data.pdStatePriorityImportant _ 0; }; Close: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; data.pdState.EndPage; data.pdState.Close; }; SpecialOp: PROC[context: Context, op: ATOM, data: REF] RETURNS [REF _ NIL] = { SELECT op FROM $NewPage => NewPage[context]; $Close => Close[context]; ENDCASE => ERROR Imager.Error[$UnimplementedSpecialOp]; }; PDClass: ImagerPrivate.Class _ NEW [ImagerPrivate.ClassRep _ [ deviceType: $PD, Init: Init, ISet: ImagerDefault.ISet, ISetReal: ImagerDefault.ISetReal, ISetInt: ImagerDefault.ISetInt, SetSampledColor: ImagerDefault.SetSampledColor, SetSampledBlack: ImagerDefault.SetSampledBlack, DoSave: ImagerDefault.DoSave, DoSaveAll: ImagerDefault.DoSaveAll, ConcatT: ImagerDefault.ConcatT, TranslateT: ImagerDefault.TranslateT, RotateT: ImagerDefault.RotateT, ScaleT: ImagerDefault.ScaleT, Scale2T: ImagerDefault.Scale2T, Move: ImagerDefault.Move, Trans: ImagerDefault.Trans, SetXY: ImagerDefault.SetXY, IntegerSetXY: ImagerDefault.IntegerSetXY, SetXYRel: ImagerDefault.SetXYRel, IntegerSetXYRel: ImagerDefault.IntegerSetXYRel, GetCP: ImagerDefault.GetCP, GetCPRounded: ImagerDefault.GetCPRounded, MaskFill: MaskFill, MaskStroke: MaskStroke, MaskStrokeClosed: MaskStrokeClosed, MaskVector: ImagerDefault.MaskVector, IntegerMaskVector: ImagerDefault.IntegerMaskVector, MaskRectangle: MaskRectangle, IntegerMaskRectangle: IntegerMaskRectangle, StartUnderline: ImagerDefault.StartUnderline, MaskUnderline: ImagerDefault.MaskUnderline, IntegerMaskUnderline: ImagerDefault.IntegerMaskUnderline, MaskPixel: MaskPixel, ClipOutline: ImagerDefault.ClipOutline, ExcludeOutline: ImagerDefault.ExcludeOutline, ClipRectangle: ImagerDefault.ClipRectangle, ExcludeRectangle: ImagerDefault.ExcludeRectangle, IntegerClipRectangle: ImagerDefault.IntegerClipRectangle, IntegerExcludeRectangle: ImagerDefault.IntegerExcludeRectangle, ShowChar: ShowChar, ShowCharacters: ShowCharacters, CorrectMask: ImagerDefault.CorrectMask, CorrectSpace: ImagerDefault.CorrectSpace, SetCorrectMeasure: ImagerDefault.SetCorrectMeasure, SetCorrectTolerance: ImagerDefault.SetCorrectTolerance, Space: ImagerDefault.Space, IntegerSpace: ImagerDefault.IntegerSpace, Correct: ImagerDefault.Correct, Reset: Reset, SetViewOrigin: SetViewOrigin, GetViewOrigin: GetViewOrigin, SetViewBox: SetViewBox, GetViewBox: GetViewBox, ClipView: ClipView, MoveSurfaceRectangle: MoveSurfaceRectangle, TestRectangle: TestRectangle, GetSurfaceBounds: GetSurfaceBounds, SpecialOp: SpecialOp ]]; ImagerPrivate.RegisterDevice[PDClass]; END. ˆImagerPDImpl.mesa Michael Plass, June 22, 1984 9:41:51 am PDT Procedures exported to ImagerPD These are surely wrong. Clipper info: Initial Client-to-view info View-to-surface info Surface-to-device info Scratch storage Cached color info The font cache Output info concatenates T with the current view-to-device transformation data.lineBuffer _ ImagerPixelMaps.Create[0, [0, 0, 1, data.surfaceBounds.fSize]]; move: PROC [s, f: REAL], line: PROC [s, f: REAL], curve: PROC [s1, f1, s2, f2, s3, f3: REAL] Transforms (x, y) to (s, f) move: PROC [s, f: REAL], line: PROC [s, f: REAL], curve: PROC [s1, f1, s2, f2, s3, f3: REAL] Transforms (x, y) to (s, f) The screen angle really needs to depend on the toner. [t.a*(v.x+data.viewOrigin.x) + t.b*(v.y+data.viewOrigin.y) + t.c, t.d*(v.x+data.viewOrigin.x) + t.e*(v.y+data.viewOrigin.y) + t.f], where t=data.surfaceToDevice. a=e=0, b=-1, d=1 a=e=1, b=d=0 Ê#l˜Jšœ™™+J˜—šÏk ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ J˜ J˜ J˜Jšœ˜Jšœ˜J˜Jšœ ˜ J˜Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—JšÏn œœ˜JšœÛ˜âJšœ˜ šœœœ ˜J˜—Jšœœ˜5Jšœœ!˜;Jšœœœ˜Jšœœœ˜Jšœ œ˜+Jšœœ˜Jšœœ˜!šœœ%˜˜>—Jšœ œœ˜/JšœC˜CJšœœ#˜-Jšœ8˜8Jšœœ ˜Jšœ œ œ˜7Jšœp˜pJšœ&˜&Jšœ˜šœœ˜šœ œ˜!Jšœ˜Jšœ˜Jšœ˜—Jšœ!˜#šœ;˜;Jšœ#˜#Jšœœ˜$Jšœ˜—šœ˜ Jšœ!˜!Jšœœ˜"Jšœd˜dJšœ˜Jšœ˜——Jšœ+˜+Jšœ+˜+Jšœ˜Jšœ˜J˜—šžœœœœ˜=Jšœœœ˜*Jšœ˜Jšœ˜Jšœ*˜*Jšœ˜Jšœ˜J˜—šžœœ˜Jšœ˜Jšœ œŸ˜/Jšœœ˜ Jšœœ˜Jšœœœœ˜Jšœ˜Jšœ œ˜"Jšœœ˜%Jš œ œœœœœœ˜CJšœC˜Cšžœœœ˜Išœœ˜Jšœ*˜.Jšœ)˜-—Jšœ˜—šžœœ œ œ˜@Jšœ¡™¡šœ˜šœ˜J™Jšœ[˜[—š˜J™ JšœZ˜Z——Jšœ˜—Jšœ6˜6JšœQ˜QšœB˜BJšœ˜Jšœe˜eJšœŸ ˜Jšœ˜—Jšœ œœœ ˜5Jšœ œ˜%Jšœ,˜,šžœœ œ œ˜3Jšœ œœ ˜.JšœI˜IJšœ9˜9šœœ œœœœœ˜EJšœœ˜!Jšœœ˜!Jšœ˜Jšœ9˜9Jšœ2˜2Jšœ2˜2Jšœ;˜;š œœœœ˜:Jšœ4˜4Jšœœ˜—šœ'œœ˜Hšœ>œ˜Fšœ˜#JšœE˜EJšœI˜IJšœ˜—Jšœ˜—Jšœ˜ Jšœ˜—šœ ˜˜ šœ˜#šœ ˜ Jšœ'œœ˜4JšœZ˜ZJ˜*J˜—šœ ˜ Jšœ'œœ˜4Jšœ[˜[J˜.J˜—Jšœœ˜ Jšœœ˜—J˜—šœ˜Jšœ,˜,J˜,J˜—Jšœ9˜9Jšœœ˜—J˜—šœ œœ˜šœœ˜"Jšœ'˜'Jšœ'˜'J˜—Jšœ/˜/J˜—šœ˜Jšœ#˜#Jšœ˜—Jšœ ˜ Jšœ ˜ J˜—Jšœ[˜[Jšœ˜J˜—š ž$œœ'œœœ˜vJšœ œ˜"Jšœ€˜€JšœW˜]Jšœ˜J˜—šž œœ,˜?Jšœ œ˜"Jšœ˜Jšœ˜J˜—šž œœœ˜HJšœ œ˜"Jšœ˜Jšœ˜J˜—šž œœ.˜>Jšœ œ˜"šœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ˜—JšœZ˜ZJšœ/˜/Jšœœ˜Jšœ*˜*Jšœ˜Jšœ˜J˜—šž œœœ˜>Jšœ œ˜"JšœK˜KJšœO˜OJšœ–˜–šœ˜Jšœ"˜"Jšœ"˜"Jšœ˜Jšœ˜Jšœ˜—Jšœ˜J˜—šžœœ4œ˜NJšœ œ˜"šœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ˜—Jšœ\˜\Jšœ0˜0Jšœ/˜/Jšœœ˜šœ˜Jšœ œ(˜7Jšœ+˜/—Jšœ˜Jšœ ˜ Jšœ˜J˜—šžœœ<˜VJšœ,˜1Jšœ˜J˜—š žœœœœœœ˜4Jšœœœœœœœ˜EJšœ˜J˜—šžœœœœ˜8Jšœœœœ˜sJšœ˜J˜—šžœœ*œœ0˜ˆJšœ/˜/Jšœœ˜Jšœœ˜Jšœœ.˜9Jšœœ0˜;Jšœœ.˜9Jšœœ0˜;Jšœœœ ˜Jšœœœ ˜Jšœœœ ˜Jšœœœ ˜JšœU˜UJšœ˜J˜—šž œœ œœ˜]Jšœ œ˜"Jšœœ˜%Jšœ5˜5Jšœ,œ˜0šœœ˜2JšœF˜F—šœ˜šžœ˜J˜ J˜J˜J˜Jšœ˜—Jšœ%œ˜*Jšœf˜fJšœ˜—JšœT˜TJšœ*˜*Jšœ˜J˜—šžœœœ˜BJšœ œ˜"Jšœ(˜(JšœO˜OJšœ]˜cJšœ˜J˜—šžœœ˜$Jšœ œ˜"Jšœ˜Jšœ˜Jšœ"˜"Jšœ˜J˜—šžœœ˜"Jšœ œ˜"Jšœ˜Jšœ˜Jšœ˜J˜—šž œœœœœœœ˜Nšœ˜J˜J˜Jšœœ'˜7—Jšœ˜J˜—šœœ˜>Jšœ˜Jšœ ˜ Jšœ˜Jšœ!˜!Jšœ˜Jšœ/˜/Jšœ/˜/Jšœ˜Jšœ#˜#Jšœ˜Jšœ%˜%Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ)˜)Jšœ!˜!Jšœ/˜/Jšœ˜Jšœ)˜)Jšœ˜Jšœ˜Jšœ#˜#Jšœ%˜%Jšœ3˜3Jšœ˜Jšœ+˜+Jšœ-˜-Jšœ+˜+Jšœ9˜9Jšœ˜Jšœ'˜'Jšœ-˜-Jšœ+˜+Jšœ1˜1Jšœ9˜9Jšœ?˜?Jšœ˜Jšœ˜Jšœ'˜'Jšœ)˜)Jšœ3˜3Jšœ7˜7Jšœ˜Jšœ)˜)Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ+˜+Jšœ˜Jšœ#˜#Jšœ˜Jšœ˜J˜—šœ&˜&J˜—Jšœ˜—…—‚Ò©Æ