DIRECTORY Char, Imager USING [Context], ImagerBox USING [Box, BoxFromExtents, Extents, Rectangle], ImagerFont USING [CorrectionType, Font, FontRep, Substitution, Typeface, TypefaceRep, XChar, XCharProc, XStringProc], ImagerFontWorks USING [MaskChar], ImagerError USING [Error], ImagerManhattan USING [BoundingBox, Destroy, Polygon], ImagerMaskCache USING [CharMask, CharMaskRep, CountRuns, MakeCharMaskProc, Parameters, RasterCharMaskFromManhattan, Run, RunsCharMaskFromManhattan], ImagerMaskCapture USING [Cant, CaptureManhattan], ImagerSample USING [WordsForMap], ImagerSys USING [RawHash], ImagerTransformation USING [Concat, Copy, Destroy, Equal, PostScale, Scale, Transformation, TransformationRep, TransformRectangle, TransformVec], ImagerTypeface USING [FindTypeface, InfoTable, InfoTableRep, SelectAlternateTypefaceMetrics, Typeface, TypefaceRep], Real USING [Ceiling, Floor], Rope USING [ActionType, FromRefText, Map, ROPE, Substr], ImagerScaled USING [FromReal], SF USING [Box, Size], Vector2 USING [Add, Mul, VEC]; ImagerFontImpl: CEDAR MONITOR IMPORTS Char, ImagerBox, ImagerFontWorks, ImagerError, ImagerManhattan, ImagerMaskCache, ImagerMaskCapture, ImagerSample, ImagerSys, ImagerTransformation, ImagerTypeface, Real, Rope, ImagerScaled, SF, Vector2 EXPORTS ImagerFont, ImagerFontWorks, ImagerTypeface ~ BEGIN CorrectionType: TYPE ~ ImagerFont.CorrectionType; Extents: TYPE ~ ImagerBox.Extents; Font: TYPE ~ ImagerFont.Font; FontRep: TYPE ~ ImagerFont.FontRep; XChar: TYPE ~ ImagerFont.XChar; XCharProc: TYPE ~ ImagerFont.XCharProc; XStringProc: TYPE ~ ImagerFont.XStringProc; Substitution: TYPE ~ ImagerFont.Substitution; Typeface: TYPE ~ REF TypefaceRep; TypefaceRep: PUBLIC TYPE ~ ImagerTypeface.TypefaceRep; -- export to ImagerTypeface Rectangle: TYPE ~ ImagerBox.Rectangle; ROPE: TYPE ~ Rope.ROPE; MapRope: PUBLIC PROC [rope: ROPE, start: INT ¬ 0, len: INT ¬ INT.LAST, charAction: XCharProc] ~ { state: {run, escape, escape2, extended, extended2} ¬ run; set: Char.CharSet ¬ 0; byteAction: PROC [c: CHAR] RETURNS [BOOL ¬ FALSE] ~ { b: BYTE ~ ORD[c]; SELECT state FROM run => IF b=255 THEN state ¬ escape ELSE { charAction[Char.Make[set: set, code: b]] }; escape => IF b=255 THEN state ¬ escape2 ELSE { set ¬ b; state ¬ run }; escape2 => IF b=0 THEN state ¬ extended ELSE ERROR ImagerError.Error[[$invalidEncoding, "invalid string-body encoding", LIST[[$rope, rope.Substr[start, len]]]]]; extended => IF b=255 THEN state ¬ escape ELSE { set ¬ b; state ¬ extended2 }; extended2 => { charAction[Char.Make[set: set, code: b]]; state ¬ extended }; ENDCASE; }; [] ¬ Rope.Map[base: rope, start: start, len: len, action: byteAction]; }; MapText: PUBLIC PROC [text: REF READONLY TEXT, start: NAT ¬ 0, len: NAT ¬ NAT.LAST, charAction: XCharProc] ~ { rem: NAT ~ text.length-start; -- bounds check state: {run, escape, escape2, extended, extended2} ¬ run; set: Char.CharSet ¬ 0; FOR i: NAT IN[0..MIN[len, rem]) DO b: BYTE ~ ORD[text[start+i]]; SELECT state FROM run => IF b=255 THEN state ¬ escape ELSE { charAction[Char.Make[set: set, code: b]] }; escape => IF b=255 THEN state ¬ escape2 ELSE { set ¬ b; state ¬ run }; escape2 => IF b=0 THEN state ¬ extended ELSE ERROR ImagerError.Error[[$invalidEncoding, "invalid string-body encoding", LIST[[$rope, Rope.FromRefText[text].Substr[start, len]]]]]; extended => IF b=255 THEN state ¬ escape ELSE { set ¬ b; state ¬ extended2 }; extended2 => { charAction[Char.Make[set: set, code: b]]; state ¬ extended }; ENDCASE; ENDLOOP; }; VEC: TYPE ~ Vector2.VEC; Transformation: TYPE ~ ImagerTransformation.Transformation; TransformationRep: TYPE ~ ImagerTransformation.TransformationRep; identityTransformation: Transformation ~ ImagerTransformation.Scale[1]; hashTableSize: NAT ~ 512; HashIndex: TYPE ~ [0..hashTableSize); HashTable: TYPE ~ REF HashTableRep; HashTableRep: TYPE ~ ARRAY HashIndex OF NodeList; NodeList: TYPE ~ LIST OF Node; Node: TYPE ~ RECORD [hash: CARDINAL, font: Font]; hashTable: HashTable ~ NEW[HashTableRep ¬ ALL[NIL]]; entries: INT ¬ 0; collisions: INT ¬ 0; misses: INT ¬ 0; Hash: PROC [typeface: Typeface, m: Transformation] RETURNS [CARDINAL] ~ { HashRec: TYPE ~ MACHINE DEPENDENT RECORD [ typeface: Typeface, transformationRep: TransformationRep ]; hashRec: HashRec ¬ [typeface, m­]; TRUSTED { RETURN [ImagerSys.RawHash[[base: LOOPHOLE[@hashRec], startIndex: 0, count: SIZE[HashRec]*BYTES[UNIT]], 0]] }; }; TypefaceFromFont: PUBLIC PROC [font: Font] RETURNS [Typeface] = { RETURN [font.typeface] }; MakeFont: PUBLIC PROC [typeface: Typeface, m: Transformation] RETURNS [Font] ~ { ENABLE UNWIND => NULL; hash: CARDINAL ~ Hash[typeface, m]; hashIndex: HashIndex ~ hash MOD hashTableSize; font: Font ¬ NIL; head: NodeList ~ hashTable[hashIndex]; UpdateFontList: ENTRY PROC ~ { FOR each: NodeList ¬ head, each.rest UNTIL each=NIL DO node: Node ~ each.first; IF node.hash = hash AND node.font.typeface = typeface AND ImagerTransformation.Equal[node.font.charToClient, m] THEN { font ¬ node.font; EXIT } ELSE misses ¬ misses+1; ENDLOOP; IF font = NIL THEN {-- not found, so make a new Font font ¬ NEW[FontRep ¬ [charToClient: ImagerTransformation.Copy[m], typeface: typeface]]; hashTable[hashIndex] ¬ CONS[[hash: hash, font: font], head]; entries ¬ entries+1; IF head#NIL THEN collisions ¬ collisions+1; }; }; IF typeface.info = NIL THEN { info: ImagerTypeface.InfoTable ~ NEW[ImagerTypeface.InfoTableRep ¬ ALL[[]]]; FOR code: BYTE IN BYTE DO char: XChar ~ Char.Make[set: 0, code: code]; exists: BOOL ~ typeface.class.Contains[typeface, char]; amplified: BOOL ~ typeface.class.Amplified[typeface, char]; correction: CorrectionType ~ typeface.class.Correction[typeface, char]; info[code] ¬ [exists: exists, amplified: amplified, correction: correction]; ENDLOOP; typeface.info ¬ info; -- A race to fill in typeface.info should be harmless, since the computed value should be equivalent each time. }; UpdateFontList[]; RETURN [font]; }; FlushFontCaches: PUBLIC ENTRY PROC ~ { hashTable­ ¬ ALL[NIL]; entries ¬ 0; collisions ¬ 0; misses ¬ 0; }; RectangleFromExtents: PROC [e: Extents] RETURNS [Rectangle] ~ INLINE { RETURN[[x: -e.leftExtent, y: -e.descent, w: e.leftExtent+e.rightExtent, h: e.descent+e.ascent]]; }; ExtentsFromRectangle: PROC [r: Rectangle] RETURNS [Extents] ~ INLINE { RETURN[[leftExtent: -r.x, rightExtent: r.x+r.w, descent: -r.y, ascent: r.y+r.h]]; }; TransformExtents: PROC [m: Transformation, e: Extents] RETURNS [Extents] ~ { RETURN[ExtentsFromRectangle[m.TransformRectangle[RectangleFromExtents[e]]]] }; Find: PUBLIC PROC [name: ROPE, substitution: ImagerFont.Substitution] RETURNS [Font] ~ { typeface: Typeface ~ ImagerTypeface.FindTypeface[name, substitution]; -- may raise Imager.Error RETURN[MakeFont[typeface, identityTransformation]]; }; Modify: PUBLIC PROC [font: Font, m: Transformation] RETURNS [modifiedFont: Font] ~ { modifiedTransformation: Transformation ~ ImagerTransformation.Concat[font.charToClient, m]; modifiedFont ¬ MakeFont[font.typeface, modifiedTransformation]; ImagerTransformation.Destroy[modifiedTransformation]; }; Scale: PUBLIC PROC [font: Font, s: REAL] RETURNS [modifiedFont: Font] ~ { modifiedTransformation: Transformation ~ ImagerTransformation.PostScale[font.charToClient, s]; modifiedFont ¬ MakeFont[font.typeface, modifiedTransformation]; ImagerTransformation.Destroy[modifiedTransformation]; }; FindScaled: PUBLIC PROC [name: ROPE, s: REAL] RETURNS [Font] ~ { RETURN[Scale[Find[name, substituteWithWarning], s]]; }; SelectAlternateMetrics: PUBLIC PROC [font: Font, alternateMetrics: ROPE] RETURNS [Font] ~ { typeface: Typeface ~ font.typeface; newTypeface: Typeface ~ ImagerTypeface.SelectAlternateTypefaceMetrics[typeface, alternateMetrics]; -- may raise Imager.Error RETURN[MakeFont[newTypeface, font.charToClient]]; }; Contains: PUBLIC PROC [font: Font, char: XChar] RETURNS [BOOL] ~ { typeface: Typeface ~ font.typeface; IF Char.Set[char]=0 AND typeface.info#NIL THEN RETURN[typeface.info[Char.Code[char]].exists] ELSE RETURN[typeface.class.Contains[typeface, char]]; }; NextChar: PUBLIC PROC [font: Font, char: XChar] RETURNS [next: XChar] ~ { typeface: Typeface ~ font.typeface; RETURN[typeface.class.NextChar[typeface, char]]; }; Escapement: PUBLIC PROC [font: Font, char: XChar] RETURNS [VEC] ~ { typeface: Typeface ~ font.typeface; escapement: VEC ~ typeface.class.Escapement[typeface, char]; RETURN[font.charToClient.TransformVec[escapement]]; }; Amplified: PUBLIC PROC [font: Font, char: XChar] RETURNS [BOOL] ~ { typeface: Typeface ~ font.typeface; IF Char.Set[char]=0 AND typeface.info#NIL THEN RETURN[typeface.info[Char.Code[char]].amplified] ELSE RETURN[typeface.class.Amplified[typeface, char]]; }; Correction: PUBLIC PROC [font: Font, char: XChar] RETURNS [CorrectionType] ~ { typeface: Typeface ~ font.typeface; IF Char.Set[char]=0 AND typeface.info#NIL THEN RETURN[typeface.info[Char.Code[char]].correction] ELSE RETURN[typeface.class.Correction[typeface, char]]; }; BoundingBox: PUBLIC PROC [font: Font, char: XChar] RETURNS [Extents] ~ { typeface: Typeface ~ font.typeface; charExtents: Extents ~ typeface.class.BoundingBox[typeface, char]; RETURN[TransformExtents[font.charToClient, charExtents]]; }; FontBoundingBox: PUBLIC PROC [font: Font] RETURNS [Extents] ~ { typeface: Typeface ~ font.typeface; IF typeface.fontExtents = NIL THEN { typeface.fontExtents ¬ NEW[Extents ¬ typeface.class.FontBoundingBox[typeface]]; }; RETURN[TransformExtents[font.charToClient, typeface.fontExtents­]]; }; Name: PUBLIC PROC [font: Font] RETURNS [ROPE] ~ { typeface: Typeface ~ font.typeface; RETURN [typeface.name]; }; Kern: PUBLIC PROC [font: Font, char, successor: XChar] RETURNS [VEC] ~ { typeface: Typeface ~ font.typeface; RETURN[typeface.class.Kern[typeface, char, successor]]; }; NextKern: PUBLIC PROC [font: Font, char, successor: XChar] RETURNS [XChar] ~ { typeface: Typeface ~ font.typeface; RETURN[typeface.class.NextKern[typeface, char, successor]]; }; Ligature: PUBLIC PROC [font: Font, char, successor: XChar] RETURNS [XChar] ~ { typeface: Typeface ~ font.typeface; RETURN[typeface.class.Ligature[typeface, char, successor]]; }; NextLigature: PUBLIC PROC [font: Font, char, successor: XChar] RETURNS [XChar] ~ { typeface: Typeface ~ font.typeface; RETURN[typeface.class.NextLigature[typeface, char, successor]]; }; CharMask: TYPE ~ ImagerMaskCache.CharMask; CharMaskRep: TYPE ~ ImagerMaskCache.CharMaskRep; viewOrigin: NAT = 10000; -- should agree with ImagerMaskCaptureImpl.viewOrigin maxSize: REAL = 600.0; boundsMin: REAL ¬ - REAL[viewOrigin]; boundsMax: REAL ¬ REAL[LAST[INT16]-viewOrigin]; RangeClip: PROC [real: REAL] RETURNS [REAL] ~ { RETURN[MIN[MAX[real, boundsMin], boundsMax]] }; CaptureChar: PUBLIC PROC [font: Font, char: XChar, parameters: ImagerMaskCache.Parameters, metricsOnly: BOOL ¬ FALSE] RETURNS [charMask: CharMask ¬ NIL] ~ { escapement: VEC ~ Escapement[font, char]; bounds: ImagerBox.Box ~ ImagerBox.BoxFromExtents[BoundingBox[font, char]]; IF ABS[escapement.x] <= 16383.0 AND ABS[escapement.y] <= 16383.0 AND bounds.xmin >= boundsMin AND bounds.ymin >= boundsMin AND bounds.xmax <= boundsMax AND bounds.ymax <= boundsMax THEN { SELECT TRUE FROM metricsOnly => NULL; (bounds.xmax - bounds.xmin <= maxSize AND bounds.ymax - bounds.ymin <= maxSize) => { typeface: Typeface ~ font.typeface; IF typeface.class.MakeCharMask # NIL THEN { charMask ¬ typeface.class.MakeCharMask[parameters, font, char]; }; IF charMask = NIL AND parameters.makeCharMask # NIL THEN { charMask ¬ parameters.makeCharMask[parameters, font, char]; }; IF charMask = NIL THEN { charMask ¬ GeneralCaptureCharMask[parameters, font, char]; }; }; ENDCASE => NULL; }; IF charMask = NIL THEN { IF metricsOnly THEN charMask ¬ NEW[CharMaskRep.culled] ELSE charMask ¬ NEW[CharMaskRep.maskNotCacheable]; charMask.box ¬ [ min: [Real.Floor[RangeClip[bounds.xmin]], Real.Floor[RangeClip[bounds.ymin]]], max: [Real.Ceiling[RangeClip[bounds.xmax]], Real.Ceiling[RangeClip[bounds.ymax]]]]; }; charMask.font ¬ font; charMask.char ¬ char; charMask.escapement.s ¬ ImagerScaled.FromReal[escapement.x]; charMask.escapement.f ¬ ImagerScaled.FromReal[escapement.y]; charMask.flags.amplified ¬ Amplified[font, char]; charMask.flags.correction ¬ Correction[font, char]; charMask.flags.missing ¬ NOT Contains[font, char]; }; GeneralCaptureCharMask: PUBLIC ImagerMaskCache.MakeCharMaskProc = { Operator: PROC [context: Imager.Context] = { ImagerFontWorks.MaskChar[font, char, context]; }; ok: BOOL ¬ TRUE; p: ImagerManhattan.Polygon ¬ NIL; charMask: ImagerMaskCache.CharMask ¬ NIL; p ¬ ImagerMaskCapture.CaptureManhattan[ operator: Operator, m: NIL, checkColor: TRUE, parameters: parameters ! ImagerMaskCapture.Cant => {ok ¬ FALSE; CONTINUE}]; IF ok THEN { bb: SF.Box = ImagerManhattan.BoundingBox[p]; bitmapWords: REAL = ImagerSample.WordsForMap[ size: SF.Size[bb], bitsPerSample: 1]; nRuns: INT = ImagerMaskCache.CountRuns[p]; rasterCost: REAL = parameters.rasterCost.slope*bitmapWords + parameters.rasterCost.offset; runsCost: REAL = parameters.runsCost.slope*nRuns*WORDS[ImagerMaskCache.Run] + parameters.runsCost.offset; uncacheableCost: REAL = parameters.uncacheableCost.offset; min: REAL = MIN[rasterCost, runsCost, uncacheableCost]; SELECT TRUE FROM (rasterCost=min) => charMask ¬ ImagerMaskCache.RasterCharMaskFromManhattan[p, bb]; (runsCost=min) => charMask ¬ ImagerMaskCache.RunsCharMaskFromManhattan[p, bb, nRuns]; ENDCASE => NULL; }; ImagerManhattan.Destroy[p]; RETURN [charMask] }; StringEscapement: PUBLIC PROC [font: Font, string: XStringProc, amplifySpace: REAL] RETURNS [VEC] ~ { sum: VEC ¬ [0, 0]; -- accumulated in character coordinates typeface: Typeface ~ font.typeface; amp: BOOL ~ amplifySpace # 1.0; charAction: XCharProc ~ { escapement: VEC ¬ typeface.class.Escapement[typeface, char]; IF amp AND Correction[font, char] = space THEN escapement ¬ Vector2.Mul[escapement, amplifySpace]; sum ¬ Vector2.Add[sum, escapement]; }; string[charAction]; RETURN[font.charToClient.TransformVec[sum]]; }; RopeEscapement: PUBLIC PROC [font: Font, rope: ROPE, start: INT, len: INT, amplifySpace: REAL] RETURNS [VEC] ~ { string: XStringProc ~ { MapRope[rope, start, len, charAction] }; RETURN[StringEscapement[font, string, amplifySpace]]; }; TextEscapement: PUBLIC PROC [font: Font, text: REF READONLY TEXT, start: NAT, len: NAT, amplifySpace: REAL] RETURNS [VEC] ~ { string: XStringProc ~ { MapText[text, start, len, charAction] }; RETURN[StringEscapement[font, string, amplifySpace]]; }; StringBoundingBox: PUBLIC PROC [font: Font, string: XStringProc, amplifySpace: REAL] RETURNS [Extents] ~ { typeface: Typeface ~ font.typeface; sxmin, symin, sxmax, symax: REAL ¬ 0; position: VEC ¬ [0, 0]; count: INT ¬ 0; amp: BOOL ~ amplifySpace # 1.0; charAction: XCharProc ~ { charExtents: Extents ~ typeface.class.BoundingBox[typeface, char]; charEscapement: VEC ¬ typeface.class.Escapement[typeface, char]; cxmin: REAL ~ position.x-charExtents.leftExtent; cxmax: REAL ~ position.x+charExtents.rightExtent; cymin: REAL ~ position.y-charExtents.descent; cymax: REAL ~ position.y+charExtents.ascent; IF count=0 THEN { sxmin ¬ cxmin; sxmax ¬ cxmax; symin ¬ cymin; symax ¬ cymax } ELSE { IF cxmin < sxmin THEN sxmin ¬ cxmin; IF cxmax > sxmax THEN sxmax ¬ cxmax; IF cymin < symin THEN symin ¬ cymin; IF cymax > symax THEN symax ¬ cymax; }; IF amp AND Correction[font, char] = space THEN charEscapement ¬ Vector2.Mul[charEscapement, amplifySpace]; position ¬ Vector2.Add[position, charEscapement]; count ¬ count+1; }; string[charAction]; RETURN[TransformExtents[font.charToClient, [leftExtent: -sxmin, rightExtent: sxmax, descent: -symin, ascent: symax]]]; }; RopeBoundingBox: PUBLIC PROC [font: Font, rope: ROPE, start: INT, len: INT, amplifySpace: REAL] RETURNS [Extents] ~ { string: XStringProc ~ { MapRope[rope, start, len, charAction] }; RETURN[StringBoundingBox[font, string, amplifySpace]]; }; TextBoundingBox: PUBLIC PROC [font: Font, text: REF READONLY TEXT, start: NAT, len: NAT, amplifySpace: REAL] RETURNS [Extents] ~ { string: XStringProc ~ { MapText[text, start, len, charAction] }; RETURN[StringBoundingBox[font, string, amplifySpace]]; }; END. „ ImagerFontImpl.mesa Copyright Σ 1984, 1985, 1986, 1988, 1989, 1990, 1991, 1993 by Xerox Corporation. All rights reserved. Doug Wyatt, June 5, 1990 4:07 pm PDT Michael Plass, May 28, 1993 9:30 am PDT N.B. Exported to ImagerTypeface Avoids some opaque-type hassles. N.B. Exported to ImagerTypeface N.B. Exported to ImagerTypeface A race to fill in typeface.fontExtents should be harmless, since the computed value should be equivalent each time. N.B. Exported to ImagerFontWorks Reasonableness check for size of cached object wrt output device If OK then check whether we really need a bitmap and whether the mask is too big for the cache (N.B. also good for PostScript setcachedevice operator!) Only the actual bitmap generation is specific to either the FontSolution or General approach; the remainder of the information is common and is dealt with below. N.B. Exported to ImagerTypeface Κ^•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ Οeœ[™fK™$K™'K™—šΟk ˜ K˜Kšœžœ ˜Kšœ žœ+˜:Kšœ žœe˜uKšœžœ ˜!Kšœ žœ ˜Kšœžœ!˜6Kšœžœ˜”Kšœžœ˜1Kšœ žœ˜!Kšœ žœ ˜Kšœžœw˜‘Kšœžœ`˜tKšœžœ˜Kšœžœ žœ ˜8Kšœ žœ ˜Kšžœžœ ˜Kšœžœ žœ˜—K˜KšΠblœžœž˜KšžœΎžœ ˜ΠKšžœ,˜3šœž˜K˜Kšœžœ˜1Kšœ žœ˜"Kšœžœ˜Kšœ žœ˜#Kšœžœ˜Kšœ žœ˜'Kšœ žœ˜+Kšœžœ˜-Kšœ žœžœ ˜!šœ žœžœΟc˜RK˜—Kšœ žœ˜&Kšžœžœžœ˜K˜šΟnœžœžœžœ žœ žœžœžœ˜aKšœ9˜9Kšœ˜š œ žœžœžœžœžœ˜5Kšœžœžœ˜šžœž˜Kšœžœžœžœ.˜VKšœ žœžœžœ˜FKš œ žœžœžœžœFžœ%˜‘Kšœ žœžœžœ ˜MKšœL˜LKšžœ˜—Kšœ˜—KšœF˜FK˜K˜—š‘œžœžœžœžœžœ žœ žœžœžœ˜oKšœžœ ˜-Kšœ9˜9Kšœ˜š žœžœžœžœ ž˜"Kšœžœžœ˜šžœž˜Kšœžœžœžœ.˜VKšœ žœžœžœ˜FKš œ žœžœžœžœFžœ7˜³Kšœ žœžœžœ ˜MKšœL˜LKšžœ˜—Kšžœ˜—K˜K˜—K˜Kšžœžœ žœ˜Kšœžœ'˜;Kšœžœ*˜AKšœG˜GK˜Kšœžœ˜Kšœ žœ˜%Kšœ žœžœ˜#Kšœžœžœ žœ ˜1Kšœ žœžœžœ˜šœžœžœžœ˜1K˜—Kšœžœžœžœ˜4Kšœ žœ˜Kšœ žœ˜Kšœžœ˜K˜š‘œžœ)žœžœ˜Iš œ žœžœž œžœ˜*Kšœ˜Kšœ$˜$Kšœ˜—Kšœ"˜"Kš žœžœžœ"žœ žœžœ ˜wK˜K˜—š‘œžœžœžœ˜AKšΟb™K™ Kšžœ˜Kšœ˜K˜—š‘œž œ)žœ ˜PKš’™Kšžœžœžœ˜Kšœžœ˜#Kšœžœ˜.Kšœ žœ˜Kšœ&˜&š‘œžœžœ˜šžœ"žœžœž˜6K˜šžœžœžœ6˜oKšžœžœ˜Kšžœ˜—Kšžœ˜—šžœžœžœ  ˜4KšœžœM˜WKšœžœ!˜ςSΤ