DIRECTORY Atom, CardTab, Char, CharCodeConvert, Imager USING [ConcatT, Context, MaskFill], ImagerBox USING [Box, BoxFromRectangle, Extents, ExtentsFromBox, Rectangle, RectangleFromBox], ImagerFont USING [CorrectionType, nullXChar, XChar], ImagerMaskCache USING [MakeCharMaskProc], ImagerPath USING [CurveToProc, LineToProc, MoveToProc, PathProc], ImagerTransformation USING [Transform, Transformation, TransformRectangle, TransformVec], ImagerType1Typeface, ImagerTypeface USING [Creator, CreatorFromFileExtension, RegisterCreator, Typeface, TypefaceClass, TypefaceClassRep, TypefaceRep], IO USING [STREAM], Type1Font USING [CharInfo, CharString, ExecuteChar, ParseFont, Type1Data], Vector2 USING [VEC]; ImagerType1TypefaceImpl: CEDAR PROGRAM IMPORTS Atom, CardTab, Char, CharCodeConvert, Imager, ImagerBox, ImagerTransformation, ImagerTypeface, Type1Font EXPORTS ImagerType1Typeface = BEGIN VEC: TYPE = Vector2.VEC; Typeface: TYPE ~ ImagerTypeface.Typeface; XChar: TYPE ~ ImagerFont.XChar; nullXChar: XChar ~ ImagerFont.nullXChar; CorrectionType: TYPE ~ ImagerFont.CorrectionType; Extents: TYPE ~ ImagerBox.Extents; Ord: PROC [xChar: XChar] RETURNS [CARD] ~ INLINE { RETURN [ORD[xChar]] }; CharData: TYPE ~ REF CharDataRep; CharDataRep: TYPE ~ ImagerType1Typeface.CharDataRep; MakeCharData: PROC [self: Type1Font.Type1Data, name: ATOM] RETURNS [CharData] ~ { m: ImagerTransformation.Transformation ~ self.FontMatrix; box: ImagerBox.Box ¬ [0, 0, 0, 0]; count: NAT ¬ 0; Pt: PROC [v: VEC] ~ { p: VEC ~ ImagerTransformation.Transform[m, v]; IF count=0 THEN box ¬ [xmin: p.x, ymin: p.y, xmax: p.x, ymax: p.y] ELSE { IF p.xbox.xmax THEN box.xmax ¬ p.x; IF p.ybox.ymax THEN box.ymax ¬ p.y; }; count ¬ count+1; }; move: ImagerPath.MoveToProc ~ { Pt[p] }; line: ImagerPath.LineToProc ~ { Pt[p1] }; curve: ImagerPath.CurveToProc ~ { Pt[p1]; Pt[p2]; Pt[p3] }; info: Type1Font.CharInfo ~ Type1Font.ExecuteChar[self, name, move, line, curve]; RETURN[NEW[CharDataRep ¬ [ name: name, escapement: ImagerTransformation.TransformVec[m, info.w], extents: ImagerBox.ExtentsFromBox[box] ]]]; }; SetFlags: TYPE = ImagerType1Typeface.SetFlags; Data: TYPE ~ REF Type1TypefaceDataRep; Type1TypefaceDataRep: TYPE ~ ImagerType1Typeface.Type1TypefaceDataRep; lost: LIST OF ATOM ¬ NIL; Lost: PROC RETURNS [LIST OF ATOM] ~ {RETURN [lost]}; -- check with debugger. DataFromType1Data: PUBLIC PROC [type1Data: Type1Font.Type1Data] RETURNS [ImagerType1Typeface.Type1TypefaceData] ~ { charDataForCode: CardTab.Ref ~ CardTab.Create[]; RETURN[NEW[Type1TypefaceDataRep ¬ [type1Data: type1Data, charDataForCode: charDataForCode, charSetNonempty: NIL]]]; }; GetNonemptySets: PROC [data: ImagerType1Typeface.Type1TypefaceData] RETURNS [REF ImagerType1Typeface.SetFlags] = { charSetNonempty: REF SetFlags ¬ data.charSetNonempty; IF charSetNonempty = NIL THEN { new: REF SetFlags = NEW[SetFlags]; EachCharString: PROC [name: ATOM, charString: Type1Font.CharString] = { ENABLE CharCodeConvert.PrivateUseFull => CONTINUE; xc: XChar = CharCodeConvert.XCharFromName[name]; new[Char.Set[xc]] ¬ TRUE; }; td: Type1Font.Type1Data = data.type1Data; td.CharStringsForAll[td, EachCharString]; data.charSetNonempty ¬ charSetNonempty ¬ new; }; RETURN [charSetNonempty] }; notdef: XChar = CharCodeConvert.XCharFromName[Atom.MakeAtom[".notdef"]]; GetCharData: PUBLIC PROC [data: ImagerType1Typeface.Type1TypefaceData, char: XChar] RETURNS [CharData] ~ { td: Type1Font.Type1Data = data.type1Data; charDataForCode: CardTab.Ref ~ data.charDataForCode; WITH CardTab.Fetch[charDataForCode, Ord[char]].val SELECT FROM cd: CharData => RETURN [cd]; ENDCASE; FOR names: LIST OF ATOM ¬ CharCodeConvert.NamesFromXChar[char], names.rest WHILE names # NIL DO name: ATOM = names.first; IF td.CharStringsKnown[td, name] THEN { cd: CharData ¬ MakeCharData[td, name]; set: CARD ~ Char.Set[char]; IF NOT CardTab.Insert[charDataForCode, Ord[char], cd] THEN lost ¬ CONS[name, lost]; RETURN [cd]; }; ENDLOOP; IF char # notdef THEN RETURN [GetCharData[data, notdef]]; RETURN [NIL]; }; Type1Contains: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ { data: Data ~ NARROW[self.data]; td: Type1Font.Type1Data = data.type1Data; charDataForCode: CardTab.Ref ~ data.charDataForCode; WITH CardTab.Fetch[charDataForCode, Ord[char]].val SELECT FROM cd: CharData => RETURN [TRUE]; ENDCASE; FOR names: LIST OF ATOM ¬ CharCodeConvert.NamesFromXChar[char], names.rest WHILE names # NIL DO IF td.CharStringsKnown[td, names.first] THEN RETURN [TRUE]; ENDLOOP; RETURN [FALSE]; }; Type1NextChar: PROC [self: Typeface, char: XChar] RETURNS [XChar] ~ { data: Data ~ NARROW[self.data]; nullSet: Char.CharSet = Char.Set[nullXChar]; set: Char.CharSet ¬ Char.Set[char]; code: BYTE ¬ Char.Code[char]; charSetNonempty: REF ImagerType1Typeface.SetFlags = GetNonemptySets[data]; DO SELECT TRUE FROM set = nullSet => code ¬ set ¬ 0; code < BYTE.LAST => code ¬ code+1; set < BYTE.LAST => {set ¬ set+1; code ¬ 0}; ENDCASE => RETURN [nullXChar]; IF charSetNonempty[set] THEN { WHILE code < BYTE.LAST DO IF Type1Contains[self, Char.Make[set, code]] THEN RETURN [Char.Make[set, code]]; code ¬ code + 1; ENDLOOP; }; ENDLOOP; }; Type1Escapement: PROC [self: Typeface, char: XChar] RETURNS [VEC] ~ { data: Data ~ NARROW[self.data]; cd: CharData ~ GetCharData[data, char]; RETURN[IF cd=NIL THEN [0, 0] ELSE cd.escapement]; }; Type1Amplified: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ { data: Data ~ NARROW[self.data]; RETURN [char = Char.Make[set: 0, code: 40B]]; }; Type1Correction: PROC [self: Typeface, char: XChar] RETURNS [CorrectionType] ~ { RETURN[IF char = Char.Make[set: 0, code: 40B] THEN space ELSE mask]; }; Type1BoundingBox: PROC [self: Typeface, char: XChar] RETURNS [Extents] ~ { data: Data ~ NARROW[self.data]; cd: CharData ~ GetCharData[data, char]; RETURN[IF cd=NIL THEN [0, 0, 0, 0] ELSE cd.extents]; }; Type1FontBoundingBox: PROC [self: Typeface] RETURNS [Extents] ~ { data: Data ~ NARROW[self.data]; type1Data: Type1Font.Type1Data ~ data.type1Data; box1: ImagerBox.Box ~ type1Data.FontBBox; rect1: ImagerBox.Rectangle ~ ImagerBox.RectangleFromBox[box1]; rect2: ImagerBox.Rectangle ~ ImagerTransformation.TransformRectangle[type1Data.FontMatrix, rect1]; box2: ImagerBox.Box ~ ImagerBox.BoxFromRectangle[rect2]; RETURN [ImagerBox.ExtentsFromBox[box2]]; }; Type1Kern: PROC [self: Typeface, char, successor: XChar] RETURNS [VEC] ~ { RETURN[[0, 0]]; }; Type1NextKern: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN[nullXChar]; }; Type1Ligature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN[nullXChar]; }; Type1NextLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN[nullXChar]; }; Type1Mask: PROC [self: Typeface, char: XChar, context: Imager.Context] ~ { data: Data ~ NARROW[self.data]; cd: CharData ~ GetCharData[data, char]; IF cd# NIL THEN { type1Data: Type1Font.Type1Data ~ data.type1Data; name: ATOM ~ cd.name; path: ImagerPath.PathProc ~ { [] ¬ Type1Font.ExecuteChar[type1Data, name, moveTo, lineTo, curveTo]; }; Imager.ConcatT[context, type1Data.FontMatrix]; Imager.MaskFill[context, path]; }; }; registeredMakeCharMask: ImagerMaskCache.MakeCharMaskProc ¬ NIL; RegisterMakeCharMask: PUBLIC PROC [makeCharMask: ImagerMaskCache.MakeCharMaskProc] ~ { registeredMakeCharMask ¬ makeCharMask; }; DispatchMakeCharMask: ImagerMaskCache.MakeCharMaskProc ~ { P: ImagerMaskCache.MakeCharMaskProc ¬ registeredMakeCharMask; IF P = NIL THEN RETURN [NIL]; RETURN P[parameters: parameters, font: font, char: char]; }; MakeType1TypefaceClass: PUBLIC PROC [type: ATOM] RETURNS [ImagerTypeface.TypefaceClass] ~ { class: ImagerTypeface.TypefaceClass ~ NEW [ImagerTypeface.TypefaceClassRep ¬ [ type: type, Contains: Type1Contains, NextChar: Type1NextChar, Escapement: Type1Escapement, Amplified: Type1Amplified, Correction: Type1Correction, BoundingBox: Type1BoundingBox, FontBoundingBox: Type1FontBoundingBox, Ligature: Type1Ligature, NextLigature: Type1NextLigature, Kern: Type1Kern, NextKern: Type1NextKern, Mask: Type1Mask, MakeCharMask: DispatchMakeCharMask ]]; RETURN [class] }; type1Class: ImagerTypeface.TypefaceClass ~ MakeType1TypefaceClass[$Type1Font]; Type1Create: PROC [stream: IO.STREAM] RETURNS [Typeface] ~ { type1Data: Type1Font.Type1Data ~ Type1Font.ParseFont[stream]; data: Data ~ DataFromType1Data[type1Data]; RETURN[NEW[ImagerTypeface.TypefaceRep ¬ [class: type1Class, data: data]]]; }; creator: ImagerTypeface.Creator ~ ImagerTypeface.CreatorFromFileExtension["psf", Type1Create]; ImagerTypeface.RegisterCreator[creator: creator, before: TRUE]; END. Template for clients: myType1Class: ImagerTypeface.TypefaceClass ~ MakeClass[]; MakeClass: PROC RETURNS [class: ImagerTypeface.TypefaceClass] ~ { class ¬ MakeType1TypefaceClass[$MyType1Font]; class.MakeCharMask ¬ MyMakeCharMask; }; MyType1Create: PROC [stream: IO.STREAM] RETURNS [Typeface] ~ { type1Data: Type1Font.Type1Data ~ Type1Font.ParseFont[stream]; data: Type1TypefaceData ~ DataFromType1Data[type1Data]; myData: NEW[MyDataRep ¬ [...]]; data.ext ¬ myData; RETURN[NEW[ImagerTypeface.TypefaceRep ¬ [class: myType1Class, data: data]]]; }; creator: ImagerTypeface.Creator ~ ImagerTypeface.CreatorFromFileExtension["mypsf", MyType1Create]; ImagerTypeface.RegisterCreator[creator, TRUE]; ΜImagerType1TypefaceImpl.mesa Copyright Σ 1990, 1991, 1993 by Xerox Corporation. All rights reserved. Michael Plass, November 10, 1993 11:49 am PST Doug Wyatt, April 30, 1993 3:39 pm PDT Russ Atkinson (RRA) May 26, 1993 3:48 pm PDT RRA, May 21, 1993 1:53:19 pm PDT Type1NextChar may not find characters that have been dynamically added to the underlying type1Data if they are in another character set, since charSetNonempty may not track the change until GetCharData is called. Is this a problem? MFP, November 10, 1993 11:18:06 am PST Yes, it is a problem. Or rather, it was. charSetNonempty is now created only when needed, by enumerating the character strings. Here we supply (or ambush) the class procs. Κ ™–(cedarcode) style•NewlineDelimiter ˜codešœ™Kšœ Οeœ<™GK™-K™&K™,K˜—šΟk ˜ K˜K˜K˜K˜Kšœžœ˜*Kšœ žœO˜^Kšœ žœ$˜4Kšœžœ˜)Kšœ žœ1˜AKšœžœ?˜YKšœ˜Kšœžœn˜‚Kšžœžœžœ˜Kšœ žœ;˜JKšœžœžœ˜K˜—šΟnœžœž˜&Kšžœi˜pKšžœ˜Kšœž˜K˜Kšžœžœ žœ˜Kšœ žœ˜)Kšœžœ˜Kšœ(˜(Kšœžœ˜1šœ žœ˜"K˜—šŸœžœžœžœžœžœžœ ˜IK˜—Kšœ žœžœ ˜!šœ žœ#˜4K˜—šŸ œžœ#žœžœ˜QK˜9K˜"Kšœžœ˜šŸœžœžœ˜Kšœžœ(˜.Kšžœ žœ3˜Bšžœ˜Kš žœžœžœžœžœ˜MKš žœžœžœžœžœ˜MKšœ˜—K˜K˜—Kšœ(˜(Kšœ)˜)Kšœ;˜;K˜Pšžœžœ˜K˜ Kšœ9˜9Kšœ&˜&Kšœ˜—K˜K˜—šœ žœ ˜.K˜—Kšœžœžœ˜&šœžœ,˜FK˜—Kš œžœžœžœžœ˜šŸœžœžœžœžœžœžœ Οc˜LK˜—šŸœžœžœ"žœ,˜sKšœ0˜0Kšžœžœbžœ˜sK˜K˜—šŸœžœ/žœžœ"˜rJšœžœ!˜5šžœžœžœ˜Kšœžœ œœžœ ˜"šŸœžœžœ'˜GJšžœ#žœ˜2J˜0Kšœžœ˜K˜—K˜)K˜)K˜-K˜—Kšžœ˜K˜K˜—˜HK˜—šŸ œžœžœ<žœ˜jK˜)K˜4šžœ/žœž˜>Kšœžœ˜Kšžœ˜—š žœžœžœžœ4žœ žœž˜_Kšœžœ˜šžœžœ˜'K˜&Kšœžœ˜šžœžœ0ž˜:Kšœžœ ˜—Kšžœ˜ K˜—Kšžœ˜—Kšžœžœžœ˜9Kšžœžœ˜ K˜K˜—šŸ œžœžœžœ˜DKšœ žœ ˜K˜)K˜4šžœ/žœž˜>Kšœžœžœ˜Kšžœ˜—š žœžœžœžœ4žœ žœž˜_Kšžœ&žœžœžœ˜;Kšžœ˜—Kšžœžœ˜K˜K˜—šŸ œžœžœ ˜E™ K™θ—™&J™—Kšœ žœ ˜K˜,K˜#Kšœžœ˜Kšœžœ6˜Jšž˜šžœžœž˜K˜ Kšœžœžœ˜"Kšœžœžœ˜+Kšžœžœ ˜—šžœžœ˜šžœžœžœž˜Kšžœ+žœžœ˜PK˜Kšžœ˜—Kšœ˜—Kšžœ˜—K˜K˜—šŸœžœžœžœ˜EKšœ žœ ˜Kšœ'˜'Kš žœžœžœžœžœ˜1K˜K˜—šŸœžœžœžœ˜EKšœ žœ ˜Kšžœ'˜-K˜K˜—šŸœžœžœ˜PKšžœžœ%žœžœ˜DK˜K˜—šŸœžœžœ˜JKšœ žœ ˜Kšœ'˜'Kš žœžœžœžœžœ ˜4K˜K˜—šŸœžœžœ˜AKšœ žœ ˜Kšœ0˜0Kšœ)˜)Kšœ>˜>Kšœb˜bKšœ8˜8Kšžœ"˜(K˜K˜—šŸ œžœ*žœžœ˜JKšžœ ˜Kšœ˜K˜—šŸ œžœ*žœ ˜PKšžœ ˜Kšœ˜K˜—šŸ œžœ*žœ ˜PKšžœ ˜Kšœ˜K˜—šŸœžœ*žœ ˜TKšžœ ˜Kšœ˜K˜—šŸ œžœ;˜JKšœ žœ ˜Kšœ'˜'šžœžœžœ˜Kšœ0˜0Kšœžœ ˜šœ˜K˜EKšœ˜—K˜.Kšœ˜Kšœ˜—K˜K˜—Kšœ;žœ˜?šŸœžœžœ5˜VKšœ&˜&Kšœ˜—šŸœ&˜:KšŸœ<˜=Kš žœžœžœžœžœ˜Kšžœ3˜9Kšœ˜K˜—š Ÿœžœžœžœžœ#˜[šœ&žœ%˜NK˜ KšŸœ˜KšŸœ˜KšŸ œ˜KšŸ œ˜KšŸ œ˜KšŸ œ˜KšŸœ˜&KšŸœ˜KšŸ œ˜ KšŸœ ˜KšŸœ˜KšŸœ ˜KšŸ œ˜"K˜—Kšžœ˜Kšœ˜K˜—šœN˜NK˜—šŸ œžœ ž œžœ˜Kšœ=˜=Kšœ7˜7Kšœžœ˜Kšœ˜KšžœžœB˜LKšœ˜K˜—šœb˜bK˜—Kšœ(žœ˜.K˜—K˜—…—$œ2