DIRECTORY Args, Buttons, Commander, G2dContour, G2dTool, Draw2d, FileNames, FS, G2dBasic, G2dMatrix, G2dOutline, G2dPopUp, G2dVector, Imager, ImagerBackdoor, ImagerBox, ImagerColor, ImagerFont, ImagerInterpress, ImagerMaskCapture, ImagerTransformation, InterpressInterpreter, IO, MessageWindow, PPreView, PPreViewClient, Real, Rope, RuntimeError, SF, TextNode, TiogaAccess, TiogaAccessViewers, TiogaImager, TIPUser, Vector2, ViewerClasses, ViewerOps, ViewerTools; G2dStrokeWarpCmdImpl: CEDAR PROGRAM IMPORTS Args, Buttons, G2dContour, G2dTool, Draw2d, FileNames, FS, G2dBasic, G2dMatrix, G2dOutline, G2dPopUp, G2dVector, Imager, ImagerBackdoor, ImagerBox, ImagerColor, ImagerFont, ImagerInterpress, ImagerMaskCapture, ImagerTransformation, InterpressInterpreter, IO, MessageWindow, PPreViewClient, Real, Rope, RuntimeError, TiogaAccess, TiogaAccessViewers, TiogaImager, TIPUser, ViewerOps, ViewerTools ~ BEGIN ButtonProc: TYPE ~ Buttons.ButtonProc; Pair: TYPE ~ G2dBasic.Pair; PairSequence: TYPE ~ G2dBasic.PairSequence; Matrix: TYPE ~ G2dMatrix.Matrix; Triple: TYPE ~ G2dMatrix.Triple; TransformProc: TYPE ~ G2dOutline.TransformProc; Box: TYPE ~ Imager.Box; Context: TYPE ~ Imager.Context; Rectangle: TYPE ~ Imager.Rectangle; Color: TYPE ~ ImagerColor.Color; Font: TYPE ~ ImagerFont.Font; Master: TYPE ~ InterpressInterpreter.Master; PreviewData: TYPE ~ PPreView.Data; ROPE: TYPE ~ Rope.ROPE; Reader: TYPE ~ TiogaAccess.Reader; VEC: TYPE ~ Vector2.VEC; Viewer: TYPE ~ ViewerClasses.Viewer; metersPerPoint: REAL ~ 0.0254/72.0; cmdOut: IO.STREAM; Coons: TYPE ~ RECORD [ box: Box, pu0, p1v, pu1, p0v: PairSequence, du, dv: NAT, ddu, ddv, uscale, vscale: REAL, c00, c10, c11, c01: Pair]; GetPersp: PROC [p1, p2, p3, p4: VEC, b: Box] RETURNS [m: Matrix] ~ { ENABLE Real.RealException, G2dMatrix.singular => { m ¬ [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]; GOTO Quit; }; r: Rectangle ¬ ImagerBox.RectangleFromBox[b]; m ¬ G2dMatrix.Invert[G2dMatrix.QuadrilateralToRectangle[p1, p2, p3, p4, r]]; EXITS Quit => NULL; }; GetCoons: PROC [c0, c1, c2, c3: PairSequence, b: Box] RETURNS [c: Coons] ~ { c.du ¬ c.dv ¬ 1000; c.ddu ¬ 1.0/REAL[c.du]; c.ddv ¬ 1.0/REAL[c.dv]; c.box ¬ b; c.uscale ¬ REAL[c.du]/(c.box.xmax-c.box.xmin); c.vscale ¬ REAL[c.dv]/(c.box.ymax-c.box.ymin); c.pu0 ¬ G2dContour.ResamplePairs[c3, c.dv+1]; c.p1v ¬ G2dContour.ResamplePairs[c0, c.dv+1]; c.pu1 ¬ G2dContour.ResamplePairs[c1, c.du+1]; c.p0v ¬ G2dContour.ResamplePairs[c2, c.dv+1]; [] ¬ G2dVector.ReverseSequence[c.pu0, c.pu0]; [] ¬ G2dVector.ReverseSequence[c.p0v, c.p0v]; c.c00 ¬ G2dVector.Midpoint[c.pu0[0], c.p0v[0]]; c.c10 ¬ G2dVector.Midpoint[c.pu0[c.du], c.p1v[0]]; c.c11 ¬ G2dVector.Midpoint[c.pu1[c.du], c.p1v[c.dv]]; c.c01 ¬ G2dVector.Midpoint[c.pu1[0], c.p0v[c.dv]]; }; Transformer: TransformProc ~ { t: Tool ¬ NARROW[clientData]; IF t.op = persp THEN { p: Triple ¬ G2dMatrix.Transform[[v.x, v.y, 1.0], t.persp]; RETURN[[p.x/p.z, p.y/p.z]]; } ELSE { c: Coons ¬ t.coons; ui: NAT ¬ Real.Fix[c.uscale*(v.x-c.box.xmin)]; vi: NAT ¬ Real.Fix[c.vscale*(v.y-c.box.ymin)]; vf: REAL ¬ v.y*c.ddv; uf: REAL ¬ v.x*c.ddu; mvf: REAL ¬ 1.0-vf; muf: REAL ¬ 1.0-uf; mufmvf: REAL ¬ muf*mvf; ufmvf: REAL ¬ uf*mvf; ufvf: REAL ¬ uf*vf; mufvf: REAL ¬ muf*vf; xv.x ¬ -0.5*t.pvRect.w+ mvf*c.pu0[ui].x+ vf*c.pu1[ui].x+ muf*c.p0v[vi].x+ uf*c.p1v[vi].x- mufmvf*c.c00.x- ufmvf*c.c10.x- mufvf*c.c01.x- ufvf*c.c11.x; xv.y ¬ -0.5*t.pvRect.h+ mvf*c.pu0[ui].y+ vf*c.pu1[ui].y+ muf*c.p0v[vi].y+ uf*c.p1v[vi].y- mufmvf*c.c00.y- ufmvf*c.c10.y- mufvf*c.c01.y- ufvf*c.c11.y; }; }; GetBounds: PROC [t: Tool] RETURNS [box: Box ¬ [0, 0, 0, 0]] ~ { SELECT TRUE FROM t.source = reader AND t.reader # NIL => { loc: TextNode.Location ¬ [NARROW[TiogaAccess.GetLocation[t.reader].node], 0]; box ¬ TiogaImager.FormatNodes[loc, [1000, 1000]].box.bounds; }; t.source = interpress AND t.interpressFile # NIL => { Operator: PROC [context: Imager.Context] ~ { Imager.SetAmplifySpace[context, 1.0]; InterpressInterpreter.DoPage[master, 1, context, NIL]; }; master: Master ¬ InterpressInterpreter.Open[t.interpressFile, NIL]; m: Imager.Transformation ¬ ImagerTransformation.Scale[150.0/0.0254]; -- ~150 pixels/inch n: Imager.Transformation ¬ ImagerTransformation.PostScale[m, 0.0254]; -- back to inches b: SF.Box ¬ ImagerMaskCapture.CaptureBounds[Operator, m ! ImagerMaskCapture.Cant => RESUME]; r: ImagerBox.Rectangle ¬ ImagerTransformation.InverseTransformRectangle[ n, ImagerBox.RectangleFromBox[[b.min.s, b.min.f, b.max.s, b.max.f]]]; box ¬ [r.x, r.y, r.x+r.w, r.y+r.h]; InterpressInterpreter.Close[master]; }; ENDCASE; }; PreviewPaint: ViewerClasses.PaintProc ~ { ENABLE UNWIND => GOTO Bad; t: Tool ¬ NARROW[whatChanged]; IF t.box = [0, 0, 0, 0] THEN t.box ¬ GetBounds[t]; IF t.box = [0, 0, 0, 0] THEN { Blink["Select some text or an Interpress name"]; RETURN; }; IF t.op = persp THEN t.persp ¬ GetPersp[t.perspPts[0], t.perspPts[1], t.perspPts[2], t.perspPts[3], t.box] ELSE t.coons ¬ GetCoons[t.coonsCrvs[0], t.coonsCrvs[1], t.coonsCrvs[2], t.coonsCrvs[3], t.box]; t.pvRect ¬ ImagerBackdoor.GetBounds[context ! Imager.Error => CONTINUE]; SELECT t.source FROM reader => G2dOutline.TransformSelected[t.reader, Transformer, context, 0.05, t]; interpress => { ipContext: Context ¬ G2dOutline.MakeTransformContext[Transformer, context, t]; cRect: Rectangle ¬ ImagerBackdoor.GetBounds[ipContext]; ipRect: Rectangle ¬ ImagerBox.RectangleFromBox[t.box]; Imager.SetColor[ipContext, Imager.black]; Imager.SetStrokeWidth[ipContext, 1.0]; Imager.ScaleT[ipContext, 1.0/Imager.metersPerPoint]; t.ipMaster ¬ InterpressInterpreter.Open[t.interpressFile, NIL]; InterpressInterpreter.DoPage[t.ipMaster, 1, ipContext, NIL]; InterpressInterpreter.Close[t.ipMaster]; }; ENDCASE; EXITS Bad => Blink["Error while painting"]; }; TransformText: TransformProc ~ { t: Triple ¬ G2dMatrix.Transform[[v.x, v.y, 1.0], NARROW[clientData, REF Matrix]­]; RETURN[[t.x/t.z, t.y/t.z]]; }; WarpText: PROC [x, y: REAL, text, out: ROPE, p1, p2, p3, p4: VEC, font: Font, rgb: Color] ~ { IPAction: PROC [context: Context] ~ { Imager.ScaleT[context, metersPerPoint]; Imager.TranslateT[context, [0.0, 0.5*11.0*Imager.pointsPerInch]]; G2dOutline.TransformRope[text, TransformText, context, x, y, font, rgb, 0.05, refM]; }; b: Box ¬ ImagerBox.BoxFromExtents[ImagerFont.RopeBoundingBox[font, text]]; refM: REF Matrix ¬ NEW[Matrix ¬ GetPersp[p1, p2, p3, p4, b]]; ref: ImagerInterpress.Ref ¬ ImagerInterpress.Create[out]; ImagerInterpress.DoPage[ref, IPAction]; ImagerInterpress.Close[ref]; }; StrokeWarpCommand: Commander.CommandProc ~ { cmdOut ¬ cmd.out; IF Args.NArgs[cmd] > 0 THEN { x, y, text, ipOut, x1, y1, x2, y2, x3, y3, x4, y4, r, g, b, fontA, size: Args.Arg; [x, y, text, ipOut, x1, y1, x2, y2, x3, y3, x4, y4, r, g, b, fontA, size] ¬ Args.ArgsGet[cmd, "%rrssrrrrrrrr-color%r[rr-font%s-size%r" ! Args.Error => {msg ¬ reason; GOTO Bad}]; { p1: VEC ¬ [x1.real, y1.real]; p2: VEC ¬ [x2.real, y2.real]; p3: VEC ¬ [x3.real, y3.real]; p4: VEC ¬ [x4.real, y4.real]; red: REAL ¬ IF r.ok THEN r.real ELSE 1.0; grn: REAL ¬ IF g.ok THEN g.real ELSE red; blu: REAL ¬ IF b.ok THEN b.real ELSE grn; ptSize: REAL ¬ IF size.ok THEN size.real ELSE 14.0; color: Color ¬ ImagerColor.ColorFromRGB[[red, grn, blu]]; fName: ROPE ¬ IF fontA.ok THEN fontA.rope ELSE "helvetica-mrr"; font: Font ¬ Imager.FindFontScaled[Rope.Concat["xerox/pressfonts/", fName], ptSize]; WarpText[x.real, y.real, text.rope, ipOut.rope, p1, p2, p3, p4, font, color ! Imager.Warning => {msg ¬ IO.PutFR1["no font: %g", IO.rope[fName]]; GOTO Bad}]; }; } ELSE { BCreate: PROC [name: ROPE, proc: ButtonProc, x, y: NAT, guarded: BOOL ¬ FALSE] ~ { [] ¬ Buttons.Create[info: [parent: t.parent, name: name, wx: x, wy: y], proc: proc, clientData: t, paint: TRUE, guarded: guarded]; }; t: Tool ¬ NEW[ToolRep]; t.directory ¬ FileNames.CurrentWorkingDirectory[]; t.parent ¬ ViewerOps.CreateViewer[ flavor: $StrokeWarp, info: [column: right, name: "StrokeWarp", openHeight: 334, data: t, iconic: TRUE]]; t.border ¬ ViewerOps.CreateViewer[ flavor: $Border, info: [parent: t.parent, name: "StrokeWarp", wx: 20, wy: 10, ww: 300, wh: 300, data: t, scrollable: FALSE]]; ViewerOps.OpenIcon[t.parent]; t.preViewer ¬ PPreViewClient.CreatePreViewerFromData[ NEW[PPreView.Rep ¬ [clientPaint: PreviewPaint, clientData: t, kind: ip[], fileInfo: NEW[PPreView.FileInfoRep ¬ [fullFName: "StrokeWarp"]]]], TRUE]; BCreate["WARP", WarpButton, 340, 285]; BCreate["Get Text", GetTextButton, 340, 255]; BCreate["Get IP", GetIPButton, 340, 225]; BCreate["Clear", ClearButton, 340, 195, TRUE]; t.typeButton ¬ Buttons.Create[info: [parent: t.parent, name: " Persp ", wx: 340, wy: 165], proc: TypeButton, clientData: t, paint: TRUE]; BCreate["Smooth", SmoothButton, 340, 135]; BCreate["HELP!", HelpButton, 340, 105]; ViewerOps.PaintViewer[t.border, client]; }; EXITS Bad => RETURN[$Failure, msg]; }; Tool: TYPE ~ REF ToolRep; ToolRep: TYPE ~ RECORD [ box: Box ¬ [0, 0, 0, 0], directory: ROPE, reader: Reader, interpressFile: ROPE, ipMaster: Master, preViewer: Viewer, parent: Viewer, border: Viewer, typeButton: Viewer, drawContext: Context, warpContext: Context, pvRect: Rectangle, op: {coons, persp} ¬ persp, source: {reader, interpress} ¬ reader, selected: NAT ¬ 0, persp: Matrix, perspIndex: INTEGER ¬ -1, coons: Coons, coonsIndex: INTEGER ¬ -1, coonsDone: BOOL ¬ FALSE, coonsCrvs: ARRAY [0..4) OF PairSequence, perspPts: ARRAY [0..4) OF Pair]; ClearButton: ButtonProc ~ { t: Tool ¬ NARROW[clientData]; IF t.op = coons THEN { FOR n: NAT IN [0..t.coonsIndex] DO t.coonsCrvs[n].length ¬ 0; ENDLOOP; t.coonsIndex ¬ -1; t.coonsDone ¬ FALSE; } ELSE t.perspIndex ¬ -1; ViewerOps.PaintViewer[t.border, client, FALSE, NIL]; }; GetTextButton: ButtonProc ~ { t: Tool ¬ NARROW[clientData]; t.source ¬ reader; t.reader ¬ TiogaAccessViewers.FromSelection[]; t.box ¬ [0, 0, 0, 0]; }; GetIPButton: ButtonProc ~ { master: Master; t: Tool ¬ NARROW[clientData]; t.interpressFile ¬ ViewerTools.GetSelectionContents[]; IF Rope.Find[t.interpressFile, "/"] = -1 AND Rope.Find[t.interpressFile, ">"] = -1 THEN t.interpressFile ¬ Rope.Concat[t.directory, t.interpressFile]; master ¬ InterpressInterpreter.Open[t.interpressFile, NIL ! FS.Error => {t.interpressFile ¬ NIL; Blink["Bad IP name"]; CONTINUE}]; IF master # NIL THEN {InterpressInterpreter.Close[master]; t.source ¬ interpress}; t.box ¬ [0, 0, 0, 0]; }; WarpButton: ButtonProc ~ { ViewerOps.PaintViewer[NARROW[clientData, Tool].preViewer, client]; }; TypeButton: ButtonProc ~ { t: Tool ¬ NARROW[clientData]; t.op ¬ IF t.op = persp THEN coons ELSE persp; Buttons.ReLabel[t.typeButton, IF t.op = persp THEN " Pesp " ELSE "Coons"]; ViewerOps.PaintViewer[t.border, client, FALSE, NIL]; }; SmoothButton: ButtonProc ~ { t: Tool ¬ NARROW[clientData]; IF t.op = persp THEN Blink["Must be in Coons mode"] ELSE { FOR i: NAT IN [0..4) DO [] ¬ G2dContour.SmoothPairs[t.coonsCrvs[i], t.coonsCrvs[i]]; ENDLOOP; ViewerOps.PaintViewer[t.border, client, FALSE, NIL]; }; }; HelpButton: ButtonProc ~ {G2dPopUp.Help["StrokeWarp"]}; PaintBorder: ViewerClasses.PaintProc ~ { Action: PROC ~ { IF t.op = coons THEN SELECT TRUE FROM t.coonsIndex = -1 => Draw2d.Clear[context]; whatChanged # NIL => { c: PairSequence ¬ t.coonsCrvs[t.coonsIndex]; IF c # NIL AND c.length > 1 THEN Draw2d.Line[context, c[c.length-2], c[c.length-1]]; }; ENDCASE => FOR n: NAT IN [0..t.coonsIndex] DO c: PairSequence ¬ t.coonsCrvs[n]; IF c # NIL THEN FOR n: NAT IN [1..c.length) DO Draw2d.Line[context, c[n-1], c[n]]; ENDLOOP; ENDLOOP ELSE SELECT TRUE FROM t.perspIndex = -1 => Draw2d.Clear[context]; ENDCASE => { FOR n: NAT IN [0..t.perspIndex) DO Draw2d.Line[context, t.perspPts[n], t.perspPts[n+1]]; ENDLOOP; IF t.perspIndex = 3 THEN Draw2d.Line[context, t.perspPts[3], t.perspPts[0]]; }; Draw2d.Label[context, [45.0, 280.0], "CW order: left, top, right, bottom"]; }; t: Tool ¬ NARROW[self.data]; Draw2d.DoWithBuffer[context, Action, whatChanged = NIL]; }; NotifyBorder: ViewerClasses.NotifyProc ~ { MovePersp: PROC ~ { t.perspPts[t.selected] ¬ p; ViewerOps.PaintViewer[t.border, client, FALSE, NIL]; }; AddPoint: PROC [] ~ { ENABLE RuntimeError.BoundsFault => GOTO Bad; IF t.op = coons THEN t.coonsCrvs[t.coonsIndex] ¬ G2dBasic.AddToPairSequence[t.coonsCrvs[t.coonsIndex], p] ELSE t.perspPts[t.perspIndex] ¬ p; ViewerOps.PaintViewer[t.border, client, FALSE, $NewPoint]; EXITS Bad => { Blink[IO.PutFR["Curve %g length exceeded at %g points", IO.int[t.coonsIndex], IO.int[t.coonsCrvs[t.coonsIndex].length]]]; t.coonsCrvs[t.coonsIndex].length ¬ 0; -- restart curve, about the best we can do here }; }; t: Tool ¬ NARROW[self.data]; mouse: TIPUser.TIPScreenCoords ¬ NARROW[input.first]; p: Pair ¬ [mouse.mouseX, mouse.mouseY]; SELECT input.rest.first FROM $leftDown => SELECT t.op FROM coons => SELECT TRUE FROM t.coonsDone => Blink["Already have four Coons curves!"]; t.coonsIndex = 3 => t.coonsDone ¬ TRUE; ENDCASE => { t.coonsIndex ¬ t.coonsIndex+1; AddPoint[]; }; ENDCASE => IF t.perspIndex = 3 THEN { min: REAL ¬ 100000.0; FOR n: NAT IN [0..t.perspIndex] DO d: REAL ¬ G2dVector.SquareDistance[p, t.perspPts[n]]; IF d < min THEN {min ¬ d; t.selected ¬ n}; ENDLOOP; t.perspPts[t.selected] ¬ p; } ELSE { t.selected ¬ t.perspIndex ¬ t.perspIndex+1; AddPoint[]; }; $leftHeld => IF t.op = persp THEN MovePersp[] ELSE IF t.coonsIndex IN [0..4) AND NOT t.coonsDone THEN AddPoint[]; $leftUp => IF t.op = coons THEN { IF t.coonsIndex = 3 THEN t.coonsDone ¬ TRUE; IF t.coonsCrvs[t.coonsIndex].length < 2 THEN { Blink[IO.PutFR1["Too few points, redraw curve %g", IO.int[t.coonsIndex]]]; t.coonsCrvs[t.coonsIndex].length ¬ 0; }; }; ENDCASE; }; Blink: PROC [r: ROPE] ~ { MessageWindow.Append[Rope.Concat["\t\t\t\t\t", r], TRUE]; MessageWindow.Blink[]; }; strokeWarpUsage: ROPE ~ " StrokeWarp (for interactive use) -- or -- StrokeWarp [-option] Options: [-color []] default is black [-font ] default is helvetica-mrr [-size ] default is 14 (pixels) x, y in pixels (72 per inch); fonts from ///7.0/Fonts/Xerox/Pressfonts/."; ViewerOps.RegisterViewerClass[$StrokeWarp, NEW[ViewerClasses.ViewerClassRec]]; ViewerOps.RegisterViewerClass[$Border, NEW[ViewerClasses.ViewerClassRec ¬ [ notify: NotifyBorder, paint: PaintBorder, tipTable: TIPUser.InstantiateNewTIPTable["G2dStrokeWarp.tip"] ]]]; G2dTool.Register["StrokeWarp", StrokeWarpCommand, strokeWarpUsage]; END. Ύ G2dStrokeWarpCmdImpl.mesa Copyright Σ 1987, 1992 by Xerox Corporation. All rights reserved. Loosely based on Michael Plass's /Ivy/Plass/Overnight/RefitImpl.mesa. Bloomenthal, September 10, 1992 4:12 pm PDT Types and Constants Warping Tioga IO.PutF[cmdOut, "box = [%g, %g, %g, %g]\n", IO.real[t.box.xmin], IO.real[t.box.ymin], IO.real[t.box.xmax], IO.real[t.box.ymax]]; ipContext: Context _ context; Imager.TranslateT[ipContext, [cRect.w/2, cRect.h/2]]; IF cRect.w # 0 AND cRect.h # 0 THEN Imager.ScaleT[ipContext, MIN[ipRect.w/cRect.w, ipRect.h/cRect.h]]; Imager.TranslateT[ipContext, [-(ipRect.x+ipRect.w/2), -(ipRect.y+ipRect.h/2)]]; Warping Text Warp Command Warp Tool Support Start Code Κͺ•NewlineDelimiter ™šœ™Jšœ Οeœ7™BJšœE™EJ™+J™—šΟk œ‹žœΉ˜ΟJ˜—šΠlnœž ˜#Jšžœώžœˆ˜‘—Jšœž˜headšΟl™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œžœžœ žœ˜Dšžœ,˜2J˜/Jšžœ˜ J˜—J˜-J˜LJšžœ žœ˜J˜J˜—š‘œžœ(žœ˜LJ˜Jšœ žœ˜Jšœ žœ˜J˜ Jšœ žœ˜.Jšœ žœ˜.J˜-J˜-J˜-J˜-J˜-J˜-J˜/J˜2J˜5J˜2J˜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šœ˜Jšœ˜Jšœ˜Jšœ ˜ —J˜——J˜J˜—š‘ œžœ žœ˜?šž˜šœžœ žœ˜)Jšœžœ-˜MJ˜žœ˜CJšœEΟc˜XJšœF’˜Wšœžœ2˜7Jšœžœ˜$—˜HJšœE˜E—J˜#J˜$J˜—Jšžœ˜—J˜J˜—š‘ œ˜)Jšžœžœžœ˜Jšœ žœ˜Jšžœžœ˜2šžœ)™+Jšžœžœžœžœ™T—šžœžœ˜Jšœ0˜0Jšžœ˜J˜—šžœ ˜JšžœV˜ZJšžΟsœ£œ£œ£œ£œ£œ£œ˜_—Jšœ>žœ˜Hšžœ ž˜JšœP˜Pšœ˜Jšœ™J˜NJ˜7J˜6J˜Jšœ)˜)Jšœ&˜&Jšœ4˜4J˜Jšœ5™5šžœ žœ ™Jšžœžœ&™G—JšœO™OJ˜Jšœ:žœ˜?Jšœ7žœ˜˜RJ˜J˜J˜—š‘ œ˜Jšœžœ&˜BJšœ˜J˜—š‘ œ˜Jšœ žœ ˜Jšœžœžœžœ˜-Jšœžœžœ žœ ˜JJšœ(žœžœ˜4Jšœ˜J˜—š‘ œ˜Jšœ žœ ˜šžœ ˜Jšžœ˜#šžœ˜šžœžœžœž˜J˜