Std/Mod/ETHConv.odc

MODULE StdETHConv;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT
		Fonts, Files, Stores, Ports, Views,
		TextModels, TextRulers, TextViews,
		Stamps := StdStamps, Clocks := StdClocks, StdFolds;

	CONST
		V2Tag = -4095; (* 01 F0 *)
		V4Tag = 496; (* F0 01 *)

	TYPE
		FontDesc = RECORD
			typeface: Fonts.Typeface;
			size: INTEGER;
			style: SET;
			weight: INTEGER
		END;

	VAR default: Fonts.Font;

	PROCEDURE Split (name: ARRAY OF CHAR; VAR d: FontDesc);
		VAR i: INTEGER; ch: CHAR;
	BEGIN
		i := 0; ch := name[0];
		WHILE (ch < "0") OR (ch >"9") DO
			d.typeface[i] := ch; INC(i); ch := name[i]
		END;
		d.typeface[i] := 0X;
		d.size := 0;
		WHILE ("0" <= ch) & (ch <= "9") DO
			d.size := d.size * 10 + (ORD(ch) - 30H); INC(i); ch := name[i]
		END;
		CASE ch OF
		  "b": d.style := {}; d.weight := Fonts.bold
		| "i": d.style := {Fonts.italic}; d.weight := Fonts.normal
		| "j": d.style := {Fonts.italic}; d.weight := Fonts.bold
		| "m": d.style := {}; d.weight := Fonts.bold
		ELSE d.style := {}; d.weight := Fonts.normal	(* unknown style *)
		END
	END Split;

	PROCEDURE ThisFont (name: ARRAY OF CHAR): Fonts.Font; 
		VAR d: FontDesc;
	BEGIN
		Split(name, d);
		IF d.typeface = "Syntax" THEN d.typeface := default.typeface END;
		IF d.size = 10 THEN d.size := default.size
		ELSE d.size := (d.size - 2) * Ports.point
		END;
		RETURN Fonts.dir.This(d.typeface, d.size, d.style, d.weight)
	END ThisFont;

	PROCEDURE ThisChar (ch: CHAR): CHAR;
	BEGIN
		CASE ORD(ch) OF
		  80H: ch := 0C4X | 81H: ch := 0D6X | 82H: ch := 0DCX
		| 83H: ch := 0E4X | 84H: ch := 0F6X | 85H: ch := 0FCX
		| 86H: ch := 0E2X | 87H: ch := 0EAX | 88H: ch := 0EEX | 89H: ch := 0F4X | 8AH: ch := 0FBX
		| 8BH: ch := 0E0X | 8CH: ch := 0E8X | 8DH: ch := 0ECX | 8EH: ch := 0F2X | 8FH: ch := 0F9X
		| 90H: ch := 0E9X
		| 91H: ch := 0EBX | 92H: ch := 0EFX
		| 93H: ch := 0E7X
		| 94H: ch := 0E1X
		| 95H: ch := 0F1X
		| 9BH: ch := TextModels.hyphen
		| 9FH: ch := TextModels.nbspace
		| 0ABH: ch := 0DFX
		ELSE
			ch := 0BFX	(* use inverted question mark for unknown character codes *)
		END;
		RETURN ch
	END ThisChar;
	
	PROCEDURE ^ LoadTextBlock (r: Stores.Reader; t: TextModels.Model);
	
	PROCEDURE StdFold (VAR r: Stores.Reader): Views.View;
		CONST colLeft = 0; colRight = 1; expRight = 2; expLeft = 3;
		VAR k: BYTE; state: BOOLEAN; hidden: TextModels.Model; fold: StdFolds.Fold;
	BEGIN
		r.ReadByte(k);
		CASE k MOD 4 OF
			| colLeft: state := StdFolds.collapsed
			| colRight: state := StdFolds.collapsed
			| expRight: state := StdFolds.expanded
			| expLeft: state := StdFolds.expanded
		END;
		IF (k MOD 4 IN {colLeft, expLeft}) & (k < 4) THEN
			hidden := TextModels.dir.New(); LoadTextBlock(r, hidden);
		ELSE hidden := NIL;
		END;
		fold := StdFolds.dir.New(state, "", hidden);
		RETURN fold;
	END StdFold;
	
	PROCEDURE LoadTextBlock (r: Stores.Reader; t: TextModels.Model);
		VAR r0: Stores.Reader; wr: TextModels.Writer;
			org, len: INTEGER; en, ano, i, n: BYTE; col, voff, ch: CHAR; tag: INTEGER;
			fname: ARRAY 32 OF CHAR;
			attr: ARRAY 32 OF TextModels.Attributes;
			mod, proc: ARRAY 32 OF ARRAY 32 OF CHAR;

		PROCEDURE ReadNum (VAR n: INTEGER);
			VAR s: BYTE; ch: CHAR; y: INTEGER;
		BEGIN
			s := 0; y := 0; r.ReadXChar(ch);
			WHILE ch >= 80X DO
				INC(y, ASH(ORD(ch)-128, s)); INC(s, 7); r.ReadXChar(ch)
			END;
			n := ASH((ORD(ch) + 64) MOD 128 - 64, s) + y
		END ReadNum;

		PROCEDURE ReadSet (VAR s: SET);
			VAR x: INTEGER;
		BEGIN
			ReadNum(x); s := BITS(x)
		END ReadSet;

		PROCEDURE Elem (VAR r: Stores.Reader; span: INTEGER);
			VAR v: Views.View; end, ew, eh, n, indent: INTEGER; eno, version: BYTE;
				p: TextRulers.Prop; opts: SET;
		BEGIN
			r.ReadInt(ew); r.ReadInt(eh); r.ReadByte(eno);
			IF eno > en THEN en := eno; r.ReadXString(mod[eno]); r.ReadXString(proc[eno]) END;
			end := r.Pos() + span;
			IF (mod[eno] = "ParcElems") OR (mod[eno] = "StyleElems") THEN
				r.ReadByte(version);
				NEW(p);
				p.valid := {TextRulers.first .. TextRulers.tabs};
				ReadNum(indent); ReadNum(p.left);
				p.first := p.left + indent;
				ReadNum(n); p.right := p.left + n;
				ReadNum(p.lead);
				ReadNum(p.grid);
				ReadNum(p.dsc); p.asc := p.grid - p.dsc;
				ReadSet(opts); p.opts.val := {};
				IF ~(0 IN opts) THEN p.grid := 1 END;
				IF 1 IN opts THEN INCL(p.opts.val, TextRulers.leftAdjust) END;
				IF 2 IN opts THEN INCL(p.opts.val, TextRulers.rightAdjust) END;
				IF 3 IN opts THEN INCL(p.opts.val, TextRulers.pageBreak) END;
				INCL(p.opts.val, TextRulers.rightFixed);
				p.opts.mask := {TextRulers.leftAdjust .. TextRulers.pageBreak, TextRulers.rightFixed};
				ReadNum(n); p.tabs.len := SHORT(n);
				i := 0; WHILE i < p.tabs.len DO ReadNum(p.tabs.tab[i].stop); INC(i) END;
				v := TextRulers.dir.NewFromProp(p);
				wr.WriteView(v, ew, eh)
			ELSIF mod[eno] = "StampElems" THEN
				v := Stamps.New();
				wr.WriteView(v, ew, eh)
			ELSIF mod[eno] = "ClockElems" THEN
				v := Clocks.New();
				wr.WriteView(v, ew, eh)
			ELSIF mod[eno] = "FoldElems" THEN
				v := StdFold(r);
				wr.WriteView(v, ew, eh);
			END;
			r.SetPos(end)
		END Elem;

	BEGIN
		(* skip inner text tags (legacy from V2) *)
		r.ReadXInt(tag);
		IF tag # V2Tag THEN r.SetPos(r.Pos()-2) END;
		(* load text block *)
		org := r.Pos(); r.ReadInt(len); INC(org, len - 2);
		r0.ConnectTo(r.rider.Base()); r0.SetPos(org);
		wr := t.NewWriter(NIL); wr.SetPos(0);
		n := 0; en := 0; r.ReadByte(ano);
		WHILE ano # 0 DO
			IF ano > n THEN
				n := ano; r.ReadXString(fname);
				attr[n] := TextModels.NewFont(wr.attr, ThisFont(fname))
			END;
			r.ReadXChar(col); r.ReadXChar(voff); r.ReadInt(len);
			wr.SetAttr(attr[ano]);
			IF len > 0 THEN
				WHILE len # 0 DO
					r0.ReadXChar(ch);
					IF ch >= 80X THEN ch := ThisChar(ch) END;
					IF (ch >= " ") OR (ch = TextModels.tab) OR (ch = TextModels.line) THEN
						wr.WriteChar(ch)
					END;
					DEC(len)
				END
			ELSE
				Elem(r, -len); r0.ReadXChar(ch)
			END;
			r.ReadByte(ano)
		END;
		r.ReadInt(len);
		r.SetPos(r.Pos() + len);
	END LoadTextBlock;

	PROCEDURE ImportOberon* (f: Files.File): TextModels.Model;
		VAR r: Stores.Reader; t: TextModels.Model; tag: INTEGER;
	BEGIN
		r.ConnectTo(f); r.SetPos(0);
		r.ReadXInt(tag);
		IF tag = ORD("o") + 256 * ORD("B") THEN
			(* ignore file header of Oberon for Windows and DOSOberon files *)
			r.SetPos(34); r.ReadXInt(tag)
		END;
		ASSERT((tag = V2Tag) OR (tag = V4Tag), 100);
		t := TextModels.dir.New();
		LoadTextBlock(r, t);
		RETURN t;
	END ImportOberon;
	

	PROCEDURE ImportETHDoc* (f: Files.File; OUT s: Stores.Store);
		VAR t: TextModels.Model;
	BEGIN
		ASSERT(f # NIL, 20);
		t := ImportOberon(f);
		IF t # NIL THEN s := TextViews.dir.New(t) END
	END ImportETHDoc;

BEGIN
	default := Fonts.dir.Default()
END StdETHConv.