DIRECTORY Basics USING [BITAND, bitsPerWord], FunctionCache USING [Cache, CompareProc, Create, Insert, Lookup], II, IIBackdoor, IIBox, IIFont, IIMaskCache USING [CharMask, CharMaskRep, Fetch, MaskCache, ScaledVec, Store], IIMaskCapture USING [CaptureChar], IIRaster USING [GetContainingBox], IIRasterShow USING [CommonChar, GetShowData, MaskCharMask, ShowData, zeroScaledVec], IISample, IIState USING [State, StateRep], IITransformation, IITypeface, Real, Scaled USING [Float, FromReal, half, MINUS, PLUS, Value], SF, Vector2 USING [Mul, VEC]; IIRasterShowImpl: CEDAR PROGRAM IMPORTS Basics, FunctionCache, II, IIBackdoor, IIFont, IIMaskCache, IIMaskCapture, IIRaster, IIRasterShow, IITransformation, IITypeface, Real, Scaled, SF, Vector2 EXPORTS II, IIRasterShow ~ BEGIN State: TYPE ~ IIState.State; StateRep: PUBLIC TYPE ~ IIState.StateRep; -- export to II.StateRep CharMask: TYPE ~ IIMaskCache.CharMask; CharMaskRep: TYPE ~ IIMaskCache.CharMaskRep; Context: TYPE ~ II.Context; CorrectionType: TYPE ~ IIFont.CorrectionType; Extents: TYPE ~ IIBox.Extents; Font: TYPE ~ IIFont.Font; RawArray: TYPE ~ IISample.RawArray; RawDescriptor: TYPE ~ IISample.RawDescriptor; ScaledVec: TYPE ~ IIMaskCache.ScaledVec; ShowData: TYPE ~ IIRasterShow.ShowData; Transformation: TYPE ~ IITransformation.Transformation; Typeface: TYPE ~ IITypeface.Typeface; VEC: TYPE ~ Vector2.VEC; XChar: TYPE ~ IIFont.XChar; bitsPerWord: NAT ~ Basics.bitsPerWord; nullXChar: XChar ~ IIFont.nullXChar; rawArraySize: NAT ~ IISample.rawArraySize; worryNat: NAT ~ LAST[NAT]/2-1; worryReal: REAL _ worryNat; zeroScaledVec: ScaledVec ~ IIRasterShow.zeroScaledVec; bitFont: II.Font ~ IITypeface.MakeFont[MakeBitTypeface[], IITransformation.Scale[1]]; MakeBitTypeface: PROC RETURNS [IITypeface.Typeface] ~ { class: IITypeface.TypefaceClass _ NEW[IITypeface.TypefaceClassRep _ [type: $BitTypeface, Contains: BitContains, NextChar: BitNextChar, Escapement: BitEscapement, Amplified: BitAmplified, Correction: BitCorrection, BoundingBox: BitBoundingBox, FontBoundingBox: BitFontBoundingBox, Ligature: BitLigature, NextLigature: BitNextLigature, Kern: BitKern, NextKern: BitNextKern, Mask: BitMask]]; typeface: IITypeface.Typeface _ NEW[IITypeface.TypefaceRep _ [class: class, data: NIL]]; RETURN [typeface] }; BitContains: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ {RETURN [char.set = 0]}; BitNextChar: PROC [self: Typeface, char: XChar] RETURNS [next: XChar] ~ {RETURN [IF char = nullXChar THEN [0, 0] ELSE IF char.set # 0 OR char.code = 255 THEN nullXChar ELSE [0, char.code+1]]}; BitEscapement: PROC [self: Typeface, char: XChar] RETURNS [VEC] ~ {RETURN [[0, 8]]}; BitAmplified: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ {RETURN [FALSE]}; BitCorrection: PROC [self: Typeface, char: XChar] RETURNS [CorrectionType] ~ {RETURN [none]}; BitBoundingBox: PROC [self: Typeface, char: XChar] RETURNS [Extents] ~ { RETURN [[leftExtent: 0, rightExtent: 1, descent: 0, ascent: 8]]; }; BitFontBoundingBox: PROC [self: Typeface] RETURNS [Extents] ~ { RETURN [[leftExtent: 0, rightExtent: 1, descent: 0, ascent: 8]]; }; BitLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN [nullXChar]; }; BitNextLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN [nullXChar]; }; BitKern: PROC [self: Typeface, char, successor: XChar] RETURNS [VEC] ~ { RETURN [[0,0]]; }; BitNextKern: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN [[0,0]]; }; BitMask: PROC [self: Typeface, char: XChar, context: II.Context] ~ { k: CARDINAL _ char.code; bloat: REAL _ 0.499/IITransformation.SingularValues[IIBackdoor.GetT[context]].x; II.SetStrokeEnd[context, round]; II.SetStrokeJoint[context, round]; II.SetStrokeWidth[context, 0]; FOR i: NAT IN [0..8) DO IF k >= 128 THEN { k _ k - 128; II.MaskRectangle[context, [x: -bloat, y: i-bloat, w: 1+2*bloat, h: 1+2*bloat]]; }; k _ 2*k; ENDLOOP; }; BitFont: PUBLIC PROC RETURNS [IIFont.Font] ~ {RETURN [bitFont]}; BasicShowChar: PUBLIC PROC [context: Context, char: XChar] ~ { state: State ~ context.state; amplifySpace: REAL ~ state.np.amplifySpace; proc: PROC ~ { font: Font ~ state.font; escapement: VEC _ IIFont.Escapement[font, char]; IF amplifySpace#1.0 AND IIFont.Amplified[font, char] THEN { escapement _ Vector2.Mul[escapement, amplifySpace]; }; II.Trans[context]; IF state.np.noImage = 0 THEN { IITypeface.MaskChar[font, char, context] }; II.SetXYRel[context, escapement]; SELECT IIFont.Correction[font, char] FROM none => NULL; space => II.CorrectSpace[context, escapement]; mask => II.CorrectMask[context]; ENDCASE => ERROR; }; II.DoSave[context, proc]; }; RasterShow: PUBLIC PROC [context: Context, string: IIFont.XStringProc, xrel: BOOL] ~ { state: State ~ context.state; showData: IIRasterShow.ShowData ~ IIRasterShow.GetShowData[context]; basic: BOOL ~ showData.fontCache = NIL; BasicCharAction: PROC [char: XChar] ~ { BasicShowChar[context, char] }; CachedCharAction: PROC [char: XChar] ~ { CachedShowChar[context, char] }; oddCharIndex: BOOL _ FALSE; XRelAction: PROC [char: XChar] ~ { IF oddCharIndex THEN II.SetXRel[context, char.code-128] ELSE (IF basic THEN BasicCharAction ELSE CachedCharAction)[char]; }; IF xrel THEN string[XRelAction] ELSE string[IF basic THEN BasicCharAction ELSE CachedCharAction]; }; CachedShowChar: PUBLIC PROC [context: Context, char: XChar] ~ { state: State ~ context.state; showData: IIRasterShow.ShowData ~ IIRasterShow.GetShowData[context]; fontCache: IIMaskCache.MaskCache ~ showData.fontCache; charMask: CharMask _ NIL; visible: BOOL _ state.np.noImage = 0; amplifying: BOOL ~ state.np.amplifySpace # 1.0; correcting: BOOL ~ state.np.correctPass # 0; escapement: VEC _ [0, 0]; charMask _ IIMaskCache.Fetch[fontCache, showData.fontAtom, char]; IF charMask = NIL THEN { cp: VEC ~ IITransformation.Transform[showData.viewToDevice, state.p.cp]; bb: IIBox.Box ~ showData.fontBB; compositeBox: SF.Box ~ showData.clipBounds; clippedBox: IIBox.Box ~ [ xmin: MAX[REAL[compositeBox.min.s], bb.xmin+cp.x], ymin: MAX[REAL[compositeBox.min.f], bb.ymin+cp.y], xmax: MIN[REAL[compositeBox.max.s], bb.xmax+cp.x], ymax: MIN[REAL[compositeBox.max.f], bb.ymax+cp.y] ]; IF clippedBox.xmin >= clippedBox.xmax OR clippedBox.ymin >= clippedBox.ymax THEN { visible _ FALSE } ELSE { charMask _ IIMaskCapture.CaptureChar[font: showData.fontAtom, char: char, ratio: showData.deviceParm.rastWeight, fontTuner: showData.deviceParm.fontTuner, metricsOnly: FALSE]; IF charMask # NIL THEN [] _ IIMaskCache.Store[fontCache, charMask]; }; IF charMask = NIL THEN { save: INT ~ state.np.noImage; IF NOT visible THEN state.np.noImage _ 1; -- Save some work if we know it's invisible BasicShowChar[context, char]; state.np.noImage _ save; RETURN }; }; IF visible THEN { ok: BOOL ~ IIRasterShow.MaskCharMask[context, charMask]; IF NOT ok THEN {BasicShowChar[context, char]; RETURN}; }; escapement _ IIBackdoor.TransformVec[ context: context, v: [x: Scaled.Float[charMask.escapement.s], y: Scaled.Float[charMask.escapement.f]], from: device, to: client]; IF amplifying AND charMask.amplified THEN { escapement _ Vector2.Mul[escapement, state.np.amplifySpace]; }; II.SetXYRel[context, escapement]; IF correcting THEN SELECT charMask.correction FROM none => NULL; space => II.CorrectSpace[context, escapement]; mask => II.CorrectMask[context]; ENDCASE => ERROR; }; VECFromSV: PROC [sv: ScaledVec] RETURNS [VEC] ~ INLINE { RETURN [[x: Scaled.Float[sv.s], y: Scaled.Float[sv.f]]] }; SVFromVEC: PROC [v: VEC] RETURNS [ScaledVec] ~ INLINE { RETURN [[s: Scaled.FromReal[v.x], f: Scaled.FromReal[v.y]]] }; SVAdd: PROC [a, b: ScaledVec] RETURNS [ScaledVec] ~ INLINE { RETURN [[s: Scaled.PLUS[a.s, b.s], f: Scaled.PLUS[a.f, b.f]]] }; SVSub: PROC [a, b: ScaledVec] RETURNS [ScaledVec] ~ INLINE { RETURN [[s: Scaled.MINUS[a.s, b.s], f: Scaled.MINUS[a.f, b.f]]] }; cpOffset: ScaledVec ~ [Scaled.half, Scaled.half]; FlushState: PUBLIC PROC [state: State, showData: ShowData] ~ { IF showData.valid THEN { state.p.cp _ IITransformation.InverseTransform[showData.viewToDevice, VECFromSV[SVSub[showData.cp, cpOffset]]]; SELECT showData.correctPass FROM 1 => state.p.correctSum _ IITransformation.InverseTransformVec[showData.viewToDevice, VECFromSV[showData.correctSum]]; ENDCASE => NULL; showData.valid _ FALSE; }; }; TryFastState: PUBLIC PROC [state: State, showData: ShowData] ~ { cp: VEC ~ IITransformation.Transform[showData.viewToDevice, state.p.cp]; showData.correctPass _ SELECT state.np.correctPass FROM 1 => 1, 2 => 2 ENDCASE => 0; showData.valid _ ( ABS[cp.x] < worryReal AND ABS[cp.y] < worryReal AND ABS[state.np.amplifySpace] < 4.0 ); IF showData.valid AND showData.correctPass = 1 THEN { s: VEC ~ IITransformation.TransformVec[showData.viewToDevice, state.p.correctSum]; showData.valid _ (ABS[s.x] < worryReal AND ABS[s.y] < worryReal); IF showData.valid THEN showData.correctSum _ SVFromVEC[s]; }; IF showData.valid AND showData.correctPass = 2 THEN { s: VEC ~ IITransformation.TransformVec[showData.viewToDevice, state.p.correctMask]; showData.valid _ (ABS[s.x] < worryReal AND ABS[s.y] < worryReal); IF showData.valid THEN { showData.correctMask _ SVFromVEC[s]; showData.lastUnCorrectedSpace _ zeroScaledVec; showData.lastCorrectedSpace _ zeroScaledVec; }; }; IF showData.valid THEN { showData.cp _ SVAdd[SVFromVEC[cp], cpOffset]; }; }; DoCharMetrics: PUBLIC PROC [state: State, showData: ShowData, m: IIMaskCache.CharMask] ~ { esc: ScaledVec _ m.escapement; IF NOT showData.valid THEN ERROR; IF m.amplified THEN { IF m # showData.lastAmpMask THEN { IF state.np.amplifySpace = 1.0 THEN { showData.lastAmp _ esc } ELSE { e: VEC ~ VECFromSV[esc]; es: VEC ~ Vector2.Mul[e, state.np.amplifySpace]; showData.lastAmp _ SVFromVEC[e]; }; showData.lastAmpMask _ m; }; esc _ showData.lastAmp; }; SELECT showData.correctPass FROM 1 => { SELECT m.correction FROM mask => state.p.correctMaskCount _ state.p.correctMaskCount+1; space => { showData.correctSum _ SVAdd[showData.correctSum, esc] }; ENDCASE => NULL; }; 2 => { SELECT m.correction FROM mask => { IF state.p.correctMaskCount#0 THEN { esc _ SVAdd[esc, showData.correctMask]; state.p.correctMaskCount _ state.p.correctMaskCount-1; }; }; space => { IF esc # showData.lastUnCorrectedSpace THEN { cs: VEC ~ state.p.correctSpace; e1: VEC ~ VECFromSV[esc]; e2: VEC ~ IITransformation.InverseTransformVec[showData.viewToDevice, e1]; e3: VEC ~ [x: (cs.x+1.0)*e2.x, y: (cs.y+1.0)*e2.y]; e4: VEC ~ IITransformation.TransformVec[showData.viewToDevice, e3]; showData.lastCorrectedSpace _ SVFromVEC[e4]; }; esc _ showData.lastCorrectedSpace }; ENDCASE => NULL; }; ENDCASE => NULL; showData.cp.s _ showData.cp.s.PLUS[esc.s]; showData.cp.f _ showData.cp.f.PLUS[esc.f]; IF ABS[showData.cp.s.integerPart] >= worryNat OR ABS[showData.cp.f.integerPart] >= worryNat THEN FlushState[state, showData]; }; RasterMask: TYPE ~ REF IIMaskCache.CharMaskRep.raster; CommonChar: TYPE ~ IIRasterShow.CommonChar; charArrayCache: FunctionCache.Cache _ FunctionCache.Create[maxEntries: 24]; Ord: PROC [char: XChar] RETURNS [CARDINAL] ~ INLINE {RETURN [LOOPHOLE[char]]}; GetCharArray: PUBLIC PROC [fontAtom: REF] RETURNS [r: REF ARRAY CommonChar OF RasterMask] ~ { compare: FunctionCache.CompareProc ~ {RETURN [argument = fontAtom]}; r _ NARROW[FunctionCache.Lookup[charArrayCache, compare].value]; IF r = NIL THEN { r _ NEW[ARRAY CommonChar OF RasterMask _ ALL[NIL]]; [] _ FunctionCache.Insert[charArrayCache, fontAtom, r, SIZE[ARRAY CommonChar OF RasterMask]]; }; }; CommonCharLookup: PUBLIC PROC [showData: ShowData, charArray: REF ARRAY CommonChar OF RasterMask, char: XChar] RETURNS [m: RasterMask _ NIL] ~ { WITH IIMaskCache.Fetch[showData.fontCache, showData.fontAtom, char] SELECT FROM r: RasterMask => { IF MAX[SF.SizeS[r.box], SF.SizeF[r.box], ABS[r.escapement.s.integerPart], ABS[r.escapement.f.integerPart]] < worryNat/8 THEN { m _ r; IF Ord[char] IN CommonChar THEN { charArray[Ord[char]] _ m; }; }; }; ENDCASE => NULL; }; FastShow: PUBLIC PROC [context: Context, string: IIFont.XStringProc, xrel: BOOL] ~ { state: State ~ context.state; showData: IIRasterShow.ShowData ~ IIRasterShow.GetShowData[context]; noImage: BOOL ~ (state.np.noImage#0); easyMetrics: BOOL ~ (state.np.amplifySpace = 1.0 AND state.np.correctPass = 0); box: SF.Box; TryScaled: PROC RETURNS [BOOL] ~ INLINE { TryFastState[state, showData]; IF showData.valid THEN { box _ IIRaster.GetContainingBox[context, [s: showData.cp.s.integerPart, f: showData.cp.f.integerPart]] }; RETURN [showData.valid]; }; IF NOT xrel AND showData.allow.rawBitmaps AND showData.fontCache # NIL AND TryScaled[] AND SF.SizeS[box] > 4 THEN { charArray: REF ARRAY CommonChar OF RasterMask _ GetCharArray[showData.fontAtom]; a: RawArray; p: POINTER TO RawDescriptor _ NIL; n: [0..rawArraySize] _ 0; Flush: PROC ~ TRUSTED INLINE { showData.deviceClass.MaskRawBitmaps[context, n, @a]; n _ 0; p _ @(a[0]) }; FastCharAction: PROC [char: XChar] ~ { m: RasterMask _ NIL; charBox: SF.Box; QuickLook: PROC RETURNS [ok: BOOL] ~ INLINE { IF Ord[char] IN CommonChar THEN { m _ charArray[Ord[char]]; IF m = NIL THEN { m _ CommonCharLookup[showData, charArray, char]; IF m = NIL THEN RETURN [FALSE] } }; charBox _ SF.Displace[m.box, [showData.cp.s.integerPart, showData.cp.f.integerPart]]; RETURN [TRUE] }; IF showData.valid AND QuickLook[] THEN { SELECT TRUE FROM noImage => NULL; SF.Inside[charBox, box] => { fSize: INTEGER ~ charBox.max.f-charBox.min.f; IF n = rawArraySize THEN Flush[]; IF fSize > 0 THEN TRUSTED { bplMask: CARDINAL ~ LAST[CARDINAL]-(bitsPerWord-1); pp: POINTER TO RawDescriptor ~ p; pp.box.min _ charBox.min; pp.box.max _ charBox.max; pp.bitsPerLine _ Basics.BITAND[LOOPHOLE[fSize+(bitsPerWord-1)], bplMask]; pp.basePointer _ @(m[0]); n _ n + 1; p _ pp + SIZE[RawDescriptor]; }; IF easyMetrics THEN { showData.cp.s _ showData.cp.s.PLUS[m.escapement.s]; showData.cp.f _ showData.cp.f.PLUS[m.escapement.f]; RETURN }; }; SF.Empty[SF.Intersect[charBox, showData.clipBounds]] => { NULL }; ENDCASE => { IF n # 0 THEN Flush[]; FlushState[state, showData]; IF NOT IIRasterShow.MaskCharMask[context, m] THEN ERROR; -- should never fail showData.valid _ TRUE; -- Everything is really still valid }; DoCharMetrics[state, showData, m]; RETURN; }; IF n # 0 THEN Flush[]; FlushState[state, showData]; CachedShowChar[context, char]; TryFastState[state, showData]; }; TRUSTED {n _ 0; p _ @(a[0])}; string[FastCharAction]; FlushState[state, showData]; IF n # 0 THEN Flush[]; } ELSE RasterShow[context, string, xrel]; }; END. (ImagerRasterShowImpl.mesa Copyright Σ 1986 by Xerox Corporation. All rights reserved. Michael Plass, November 26, 1986 4:22:52 pm PST BitFont Show Fast Show N.B. Uses the fact that ref assignment is atomic to avoid the need for a monitor. Can't overflow here because we were not clipped. Κ˜codešœ™Kšœ<™Kšœ˜Kšœœ˜+šœœ˜Kšœ˜Kšœ œ!˜0šœœœ˜;Jšœ3˜3Jšœ˜—Kšœ˜šœœ˜Kšœ(˜(Kšœ˜—Kšœ˜!šœ˜)Kšœœ˜ Kšœ œ#˜.Kšœœ˜ Kšœœ˜—Kšœ˜—Kšœ˜Kšœ˜K˜—š  œœœ6œ˜VKšœ˜KšœD˜DKšœœœ˜'Kš œœ2˜GKš œœ3˜IKšœœœ˜š  œœ˜"Jšœœœ!œœœœ˜yJšœ˜—Kš œœœœœœ˜aKšœ˜K˜—š œœœ$˜?Kšœ˜KšœD˜DKšœ6˜6Kšœœ˜Kšœ œ˜%Kšœ œ˜/Kšœ œ˜,Kšœ œ ˜KšœA˜Ašœ œœ˜KšœœA˜HKšœ ˜ Kšœ+˜+šœ˜Kšœœœ$˜2Kšœœœ$˜2Kšœœœ$˜2Kšœœœ#˜1Kšœ˜—šœ$œ#˜KKšœ œ˜šœ˜Kšœ¨œ˜―Kšœ œœ-˜CKšœ˜——šœ œœ˜Kšœœ˜Kšœœ œŸ+˜UKšœ˜Kšœ˜Kš˜Kšœ˜—Kšœ˜—šœ œ˜Jšœœ0˜8Jšœœœ œ˜6Kšœ˜—šœ%˜%Kšœ˜KšœT˜TKšœ˜—šœ œœ˜+Kšœ<˜šœœ˜Kšœo˜ošœ˜ Kšœv˜vKšœœ˜—Kšœœ˜Kšœ˜—Kšœ˜K˜—š  œœœ'˜@JšœœA˜HKšœœœœ˜Tšœ˜Kšœ˜Kšœ˜Kšœ˜ K˜—šœœœ˜5KšœœL˜RKšœœœ˜AKšœœ$˜:Kšœ˜—šœœœ˜5KšœœM˜SKšœœœ˜Ašœœ˜Kšœ$˜$Kšœ.˜.Kšœ,˜,Kšœ˜—Kšœ˜—šœœ˜Kšœ-˜-Kšœ˜—Kšœ˜K˜—š  œœœ@˜ZKšœ˜Kšœœœœ˜!šœ œ˜šœœ˜"šœ˜Kšœ˜šœ˜Kšœœ˜Kšœœ)˜0Kšœ ˜ Kšœ˜——Kšœ˜Kšœ˜—Kšœ˜Kšœ˜—šœ˜ ˜šœ˜Kšœ>˜>KšœC˜CKšœœ˜—K˜—˜šœ˜šœ ˜ šœœ˜$Kšœ'˜'Kšœ6˜6K˜—Kšœ˜—šœ ˜ šœ%œ˜-Kšœœ˜Kšœœ˜KšœœC˜JKšœœ,˜3Kšœœ<˜CKšœ,˜,Kšœ˜—Kšœ!˜!Kšœ˜—Kšœœ˜—K˜—Kšœœ˜—Kšœœ˜*Kšœœ˜*Kš œœ(œœ(œ˜}Kšœ˜K˜—Kšœ œœ ˜6Kšœ œ˜+KšœK˜KKš œœœœœœœ ˜Nš  œœœ œœœœ œ˜]Kšœ&œ˜DKšœœ6˜@šœœœ˜Kš œœœ œœœ˜3Kšœ7œœ œ˜]Kšœ˜—Kšœ˜K˜—š œœœ!œœ œœœ˜šœ@œ˜Ošœ˜šœœœœœœ+œ˜~Kšœ˜šœ œ œ˜!Kšœ˜K™QKšœ˜—Kšœ˜—Kšœ˜—Kšœœ˜—Kšœ˜K˜—š œœœ6œ˜TKšœ˜KšœD˜DKšœ œ˜%Kšœ œ œ˜OKšœœ˜ š   œœœœœ˜)Jšœ˜Kšœœl˜‚Kšœ˜Jšœ˜—šœœœœœœ œœœ˜sKšœ œœ œ.˜PK˜ Kšœœœœ˜"K˜š œœœ˜KšœL˜L—š œœ˜&Kšœœ˜Kšœ œ˜š   œœœœœ˜-šœ œ œ˜!Kšœ˜Kšœœœ4œœœœœ˜cKšœ˜—Kšœ œI˜UKšœœ˜ Kšœ˜—šœœ œ˜(šœœ˜Kšœ œ˜šœ˜Kšœœ˜-Kšœœ ˜!šœ œœ˜Kšœ œœœ˜3Kšœœœ˜!Kšœ˜Kšœ˜Kšœœœ"˜IKšœ˜K˜ Kšœ œ˜Kšœ˜—šœ œ˜K™0Kšœœ˜3Kšœœ˜3Kš˜Kšœ˜—K˜—Kšœœ/œ˜Ašœ˜ Kšœœ ˜Kšœ˜Kš œœ'œœŸ˜MKšœœŸ#˜:Kšœ˜——Jšœ"˜"Kšœ˜Kšœ˜—Kšœœ ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜Kšœ˜Kšœ˜Kšœœ ˜Kšœ˜—Kšœ#˜'Kšœ˜——K˜K˜Kšœ˜—…—8šKR