program _04tab;
const
	maxstr=10000;
	maxsl=50;
	maxrad=50;
	maxspan=10;			{u  bunky}
	maxomezeni=100;			{maximalne druhu spanu}
	maxkontr=16;			{kontrolnich sekvenci}
	mindelsl=10;			{pri zarovnavani dolu}
	maxbreakradku=100;		{pri rozsekavani}
	kontr:array[1..maxkontr]of string[10]=(
		'LT','GT','AMP',	{nedostane se k parseru}
		'TR','TH','TD',		{567}
		'ALIGN','LEFT','CENTER','RIGHT', {8901}
		'ROWSPAN','COLSPAN',		{23}
		'VALIGN','TOP','MIDDLE','BOTTOM');	{4567}
	specchar:array[1..3]of char=(
		'<','>','&');
	breakline=false;
type
	pchararray=^tchararray;
	tchararray=array[0..maxstr]of char;
	pcell	= ^tcell;
	tcell	= record
		rad,sl:integer; {0,0 znamena pokracovani predchozi bunky}
		hzar,vzar:shortint;
		delstr:word;
		s:pchararray;
		pocrad:byte;		{po rozsekani}
		break:array[0..maxbreakradku]of integer;
	end;
	tline=record
		pocsl:integer;
		sl:array[1..maxsl]of pcell;
	end;
	ttable=record
	{spanning je jako 1 bunka}
		pocrad:integer;
		rad:array[1..maxrad]of ^tline;
	end;
	ttable1=record
		pocrad,pocsl:integer;
		bun:array[1..maxrad,1..maxsl]of record
			orig:byte;	{0..normalni, 1..spojena (1), 2..spojena dalsi
					3..jeste nic}
			x,y:integer;
		end;
		sirsl:array[1..maxsl]of integer;
		vysrad:array[1..maxrad]of integer;
	end;
var
	delkaradku	: integer;
	odkud,kam	: text;
	tab:ttable;
	tab1:ttable1;
	omezeni:record
		pocet:integer;
		o:array[1..maxomezeni,1..3]of integer;
	end;
	mem,max:longint;

procedure error(text:string);
begin
	writeln(text);
	halt(1);
end;

procedure getparams;
var
	i:byte;
	ch:integer;
begin
	mem:=memavail;
	max:=maxavail;

	i:=ParamCount;
	if (i<2)or(i>3) then
		error('Invalid count of parameters');
	if i=2 then begin
		delkaradku:=80;
	end else begin
		val(paramstr(1),delkaradku,ch);
		if (ch<>0)or(delkaradku<1)or(delkaradku>50000) then
			error('Invalid length of line');
	end;
	assign(odkud,paramstr(i-1));
	{$i-} reset(odkud); {$i+}
	if ioresult<>0 then error('Invalid input file');
	assign(kam,paramstr(i));
	{$i-} rewrite(kam); {$i+}
	if ioresult<>0 then begin
		close(odkud);
		error('Invalid output file');
	end;

	tab.pocrad:=0;
end;

procedure closeall;
var
	sl,rad:integer;
begin
	close(odkud);
	close(kam);
	for rad:=1 to tab.pocrad do begin
		for sl:=1 to tab.rad[rad]^.pocsl do begin
			freemem(tab.rad[rad]^.sl[sl]^.s,
				tab.rad[rad]^.sl[sl]^.delstr);
			dispose(tab.rad[rad]^.sl[sl]);
		end;
		dispose(tab.rad[rad]);
	end;

	if (mem<>memavail) then
		writeln('mem: ',mem,' ',memavail,' ',mem-memavail);
	if (max<>maxavail) then
		writeln('max: ',max,' ',maxavail,' ',max-maxavail);
end;

procedure skipspace(lret:byte; var ret:string; var idx:byte);
begin
	while (idx<=lret)and(ret[idx]in[' ',#9]) do
		inc(idx);
end;

procedure parse(lret:byte; var ret:string; control:boolean; var idx,typ:byte; var r:string);
{0..konec, 1..<, 2..>, 3..=, 4..text, vyse..kontrolni sekvence}
var
	i:byte;
	amper:boolean;
begin
	skipspace(lret,ret,idx);
	if idx>lret then begin
		typ:=0;
		exit;
	end;
	if ret[idx]='<' then begin
		typ:=1;
		inc(idx);
		exit;
	end else if ret[idx]='>' then begin
		typ:=2;
		inc(idx);
		exit;
	end else if ret[idx]='=' then begin
		typ:=3;
		inc(idx);
		exit;
	end else begin
		if ret[idx]='&' then begin
		{nastavi na true, muze byt nastaveno uz pri volani}
			control:=true;
			inc(idx);
			amper:=true;
		end else
			amper:=false;
		r:='';
		while (idx<=lret)and ((r='')or not(ret[idx]in[' ',#9,'<','>','=','&',';'])) do begin
			if control then
				r:=r+upcase(ret[idx])
			else
				r:=r+ret[idx];
			inc(idx);
		end;
		if control then begin
			i:=1;
			while (i<=maxkontr)and(r<>kontr[i]) do
				inc(i);
			if i>maxkontr then begin
{protest: ve vstupnich datech pouzivate & jako predpona prikazu;
 samotny & se vysazi jako &amp; ve zdrojaku je pouzito prikazu \&, pricemz
 reakce programu neni definovana; ja pak zkopiruji tento neznamy prikaz
 na vystup bez zmen}
				typ:=4;
				if amper then
					r:='&'+r;
				exit;
			end;
			if i<=3 then begin
				r:=specchar[typ];
				typ:=4;
			end else
				typ:=i-3+4;
		end else begin
			typ:=4;
		end;
	end;
end;

procedure getnexttoken(var lret:byte; var ret:string; control:boolean; var idx,typ:byte; var r:string);
begin
	if idx>lret then begin
		if eof(odkud) then begin
			typ:=0;
			exit;
		end;
		readln(odkud,ret);
		lret:=length(ret);
		idx:=1;
	end;
	parse(lret,ret, control, idx,typ,r);
	skipspace(lret,ret, idx);
end;

procedure prenescell(var odkud:tchararray; delcell:word;
	var kam:tcell);
begin
	if delcell=0 then
		kam.s:=nil
	else begin
		getmem(kam.s,
			delcell);
		move(odkud,kam.s^,
			delcell);
		kam.delstr:=delcell;
	end;
end;

procedure readinput;
var
	ret,r:string;
	lret:byte;
	idx:byte;
	oldtyp,typ:byte;
	control:boolean;
	tabrad,tabsl:integer;
	cell:tchararray;
	delcell:word;
	cis,ch:integer;
begin
	ret:='';
	lret:=0;
	idx:=1;
	control:=false;
	tabrad:=0;
	tabsl:=-1;
	while (idx<=lret)or not eof(odkud) do begin
		getnexttoken(lret,ret, control, idx,typ,r);
		if typ=0 then continue;
		if typ=1 then begin control:=true; continue; end;
		if typ=2 then begin control:=false; continue; end;
		if typ=3 then begin {=}
			typ:=4;
			r:='=';
		end;
		if typ=5 then begin {TR prvni nebo dalsi radek}
			if tabsl>0 then
				prenescell(cell,delcell,
					tab.rad[tabrad]^.sl[tabsl]^);
			tabsl:=0;
			inc(tabrad);
			tab.pocrad:=tabrad;
			if tabrad>maxrad then error('Too much lines');
			new(tab.rad[tabrad]);
			tab.rad[tabrad]^.pocsl:=0;
			continue;
		end;
		if tabrad=0 then error('Line must begin by <TR>');
			{jen na zacatku fajlu}
		if typ in [6,7] then begin
			if tabsl>0 then
			{prenos predchoziho textu}
				prenescell(cell,delcell,
					tab.rad[tabrad]^.sl[tabsl]^);
			inc(tabsl);
			if tabsl>maxsl then error('Too many columns');
			tab.rad[tabrad]^.pocsl:=tabsl;
			new(tab.rad[tabrad]^.sl[tabsl]);
			with tab.rad[tabrad]^.sl[tabsl]^ do begin
				rad:=1;
				sl:=1;
				if typ=6 then begin
					hzar:=0;
					vzar:=0;
				end else begin
					hzar:=-1;
					vzar:=-1;
				end;
				delstr:=0;
			end;
			delcell:=0;
			continue;
		end;
		if typ in [9,10,11,15,16,17] then error('Invalid command '+r);
		if typ=8 then begin {align}
			oldtyp:=typ;
			getnexttoken(lret,ret, control, idx,typ,r);
			if typ<>3 then error('There must be =');
			getnexttoken(lret,ret, control, idx,typ,r);
			if not(typ in [9,10,11]) then
				error('There must be left, center or right');
			tab.rad[tabrad]^.sl[tabsl]^.hzar:=typ-10;
			continue;
		end;
		if typ=14 then begin {valign}
			oldtyp:=typ;
			getnexttoken(lret,ret, control, idx,typ,r);
			if typ<>3 then error('There must be =');
			getnexttoken(lret,ret, control, idx,typ,r);
			if not(typ in [15,16,17]) then
				error('There must be top, middle or bottom');
			tab.rad[tabrad]^.sl[tabsl]^.vzar:=typ-16;
			continue;
		end;
		if typ in [12,13] then begin {span}
			oldtyp:=typ;
			getnexttoken(lret,ret, control, idx,typ,r);
			if typ<>3 then error('There must be =');
			getnexttoken(lret,ret, control, idx,typ,r);
			if typ<>4 then
				error('There must number after Xspan');
			val(r,cis,ch);
			if (ch<>0)or(cis<1)or(cis>maxspan) then
				error('Invalid number of cells to span');
			if oldtyp=12 then
				tab.rad[tabrad]^.sl[tabsl]^.rad:=cis
			else
				tab.rad[tabrad]^.sl[tabsl]^.sl:=cis;
			continue;
		end;
		if typ<>4 then error('internal error in parser');
		{vlozeni retezce}
		if delcell>0 then r:=' '+r;
		if delcell+length(r)>maxstr then
			error('Too long cell');
		move(r[1],cell[delcell],length(r));
		inc(delcell,length(r));
	end;
	if tabsl>0 then
		prenescell(cell,delcell,
			tab.rad[tabrad]^.sl[tabsl]^);
end;

procedure computetable1;
{kontroluje spanning}
var
	irad,isl,aktsl,i,j:integer;

	procedure nextsl(kolik:integer);
	{zkontroluje, zda se to vleze a pripadne to posune}
	var
		i:byte;
	begin
		if aktsl=0 then aktsl:=1;
		while (aktsl<=maxsl)and(tab1.bun[irad,aktsl].orig<>3) do
			inc(aktsl);
		if aktsl>maxsl then error('Too much columns');
		for i:=0 to kolik-1 do
			if (aktsl+i>maxsl)or(tab1.bun[irad,aktsl+i].orig<>3) then
				error('Colission in spannings');
		{jinak vrati aktsl}
	end;

begin
	tab1.pocrad:=0;
	tab1.pocsl:=0;
	for irad:=1 to maxrad do
	for isl:=1 to maxsl do
	{vynulovat pole}
		tab1.bun[irad,isl].orig:=3;
	for irad:=1 to tab.pocrad do begin
		aktsl:=0;
		for isl:=1 to tab.rad[irad]^.pocsl do
		with tab.rad[irad]^.sl[isl]^ do begin
			if irad+rad-1>tab1.pocrad then begin
				if irad+rad-1>maxrad then
					error('Too much lines');
				tab1.pocrad:=irad+rad-1;
			end;
			nextsl(sl);
			if sl=1 then
				for j:=0 to rad-1 do begin
					if rad=1 then
						tab1.bun[irad+j,aktsl].orig:=0
					else if j=0 then
						tab1.bun[irad+j,aktsl].orig:=1
					else
						tab1.bun[irad+j,aktsl].orig:=2;
					tab1.bun[irad+j,aktsl].y:=irad;
					tab1.bun[irad+j,aktsl].x:=isl;
				end
			else
				for j:=0 to rad-1 do begin
					for i:=0 to sl-1 do begin
						tab1.bun[irad,aktsl+i].orig:=2;
						tab1.bun[irad,aktsl+i].y:=irad;
						tab1.bun[irad,aktsl+i].x:=isl;
					end;
					tab1.bun[irad,aktsl].orig:=1;
				end;
		end;
		if aktsl>tab1.pocsl then
			tab1.pocsl:=aktsl;
	end;

	for isl:=1 to tab1.pocsl do
		tab1.sirsl[isl]:=0;
	omezeni.pocet:=0;
	for irad:=1 to tab1.pocrad do begin
		aktsl:=1;
		while aktsl<=tab1.pocsl do
		with tab1.bun[irad,aktsl] do begin
			if orig=0 then begin
			{jedna bunka}
				if tab.rad[y]^.sl[x]^.delstr>tab1.sirsl[aktsl] then
					tab1.sirsl[aktsl]:=tab.rad[y]^.sl[x]^.delstr;
				inc(aktsl);
			end else begin
				i:=1;
				while (i<=omezeni.pocet)and
				((omezeni.o[i,1]<>tab.rad[y]^.sl[x]^.rad)or
				(omezeni.o[i,2]<>tab.rad[y]^.sl[x]^.sl)) do
					inc(i);
				if i>omezeni.pocet then begin
					inc(omezeni.pocet);
					if omezeni.pocet>maxomezeni then
						error('Too much spans');
					omezeni.o[omezeni.pocet,1]:=aktsl;
					omezeni.o[omezeni.pocet,2]:=tab.rad[y]^.sl[x]^.sl;
					omezeni.o[omezeni.pocet,3]:=tab.rad[y]^.sl[x]^.delstr;
				end else begin
					if tab.rad[y]^.sl[x]^.delstr>omezeni.o[i,3] then
						omezeni.o[i,3]:=tab.rad[y]^.sl[x]^.delstr;
				end;
				inc(aktsl,tab.rad[y]^.sl[x]^.sl);
			end;
		end;
	end;
end;

procedure computetable2;
{vypocita, jak omezit ktere sloupce}
var
	celkdel,i,pocet,irad,isl,novadelka:integer;
	newsl:integer;
begin
	celkdel:=1;
	for isl:=1 to tab1.pocsl do
		inc(celkdel,tab1.sirsl[isl]+3);
	if celkdel<=delkaradku then
	{je to genialni}
		exit;
	pocet:=0;
	novadelka:=delkaradku-1;
	for isl:=1 to tab1.pocsl do
		if tab1.sirsl[isl]>mindelsl then
			inc(pocet)
		else
			dec(novadelka,tab1.sirsl[isl]+3);
{todo! pozor, kdyz bude omezeni jenom v nadpisu!!!!!}
	if (pocet=0)or(novadelka div pocet-3<mindelsl) then
		error('Cannot break lines into given length');
	newsl:=(novadelka-1) div pocet-3;
	for isl:=1 to tab1.pocsl do begin
		if tab1.sirsl[isl]>mindelsl then
			tab1.sirsl[isl]:=newsl;
		if tab1.sirsl[isl]>250 then
			tab1.sirsl[isl]:=250;
			{kvuli operacim se stringy}
	end;
end;

procedure computetable3;
{naseka dlouhe retezce do radku}
var
	irad,isl,i:integer;
	povdel,sek,poslsek,poslmez,poslpoml:integer;
begin
	for irad:=1 to tab1.pocrad do
	for isl:=1 to tab1.pocsl do
	if tab1.bun[irad,isl].orig in [0,1] then {prvni text nebo jednobunkovy}
	with tab.rad[tab1.bun[irad,isl].y]^.sl[tab1.bun[irad,isl].x]^ do begin
	{rozseknuti 1 retezce}
		povdel:=-3;
		for i:=0 to sl-1 do
			inc(povdel,tab1.sirsl[isl+i]+3);
			{na jakou delku to budeme sekat}
		if delstr<=povdel then begin
			pocrad:=1;
			break[0]:=0;
			break[1]:=delstr;
		end else begin	{budeme sekat}
			pocrad:=0;
			poslsek:=0;
			sek:=-1;
			poslmez:=-1;
			poslpoml:=-1;
			break[0]:=0;
			while sek<delstr do begin
				inc(sek);
				if s^[sek]=' ' then begin
					poslmez:=sek;
					poslpoml:=sek;
				end else if s^[sek]='-' then
					poslpoml:=sek;
				if (sek>poslsek+povdel)or(sek>=delstr) then begin {break}
					if sek>=delstr then begin
						inc(pocrad);
						break[pocrad]:=sek;
						continue;
					end;
					if poslpoml<poslsek then
						error('Cannot break line');
					if poslmez>poslsek then
						poslpoml:=poslmez;
					inc(pocrad);
					if pocrad>maxbreakradku then
						error('Cannot break line --- it''s too long');
					break[pocrad]:=poslpoml;
					sek:=poslpoml;
					poslsek:=sek;
					poslmez:=sek-1;
					poslpoml:=sek-1;
				end;
			end;
		end;

	end;

	for irad:=1 to tab1.pocrad do
		tab1.vysrad[irad]:=1;

	{vysky jednoradkovych textu}
	for irad:=1 to tab1.pocrad do
	for isl:=1 to tab1.pocsl do
	if tab1.bun[irad,isl].orig in [0,1] then {prvni text nebo jednobunkovy}
	with tab.rad[tab1.bun[irad,isl].y]^.sl[tab1.bun[irad,isl].x]^ do begin
		if rad=1 then begin
			if pocrad>tab1.vysrad[irad] then
				tab1.vysrad[irad]:=pocrad;
		end;
	end;

	{vysky viceradkovych textu}
	for irad:=1 to tab1.pocrad do
	for isl:=1 to tab1.pocsl do
	if tab1.bun[irad,isl].orig in [0,1] then {prvni text nebo jednobunkovy}
	with tab.rad[tab1.bun[irad,isl].y]^.sl[tab1.bun[irad,isl].x]^ do begin
		if rad>1 then begin
			sek:=-1;
			for i:=0 to rad-1 do
				inc(sek,tab1.vysrad[irad+i]+1);
			while pocrad>sek do
			{oddelat vice radku}
				for i:=0 to rad-1 do begin
					inc(tab1.vysrad[irad+i]);
					inc(sek);
				end;
		end;
	end;
end;

procedure vypisbunku(irad,cisrad:integer; var sl:integer; typ:byte);
{opravdova bunka se zjisti z tab1, tam se vypocita, ktery to je radek
a do kolika sloupcu se to ma vtestnat}
var
	cell:pcell;
	i:byte;
	puvirad,rad,celkdel,celkvys:integer;
	ret:string;
begin
	if tab1.bun[irad,sl].orig=3 then begin {nic tam neni}
		write(kam,'|');
		for i:=1 to tab1.sirsl[sl] do
			write(kam,' ');
		inc(sl);
		exit;
	end;
	puvirad:=irad;
	while tab1.bun[irad,sl].orig=2 do begin {posun nahoru}
		inc(cisrad,tab1.vysrad[irad]+1);
		dec(irad);
	end;
	cell:=tab.rad[tab1.bun[irad,sl].y]^.sl[tab1.bun[irad,sl].x];
	if (typ=1)or((tab1.bun[puvirad,sl].orig=1)
	and(puvirad+1<=tab1.pocrad)and(tab1.bun[puvirad+1,sl].orig=2)) then begin
	{normalni bunka nebo deleny text}
		celkvys:=-1;
		for i:=0 to cell^.rad-1 do
			inc(celkvys,tab1.vysrad[irad+i]+1);
		case cell^.vzar of
			-1:rad:=cisrad;	{nahoru}
			0:rad:=cisrad-(celkvys-cell^.pocrad) div 2;
			1:rad:=cisrad-(celkvys-cell^.pocrad);
		end;
		if (rad>=1)and(rad<=cell^.pocrad) then begin
			move(cell^.s^[cell^.break[rad-1]],
				ret[1],cell^.break[rad]-cell^.break[rad-1]);
			ret[0]:=char(cell^.break[rad]-cell^.break[rad-1]);
			while ret[length(ret)]in[' ',#9] do
				dec(ret[0]);
			while ret[1]in[' ',#9] do
				delete(ret,1,1);
		end else
			ret:='';
		celkdel:=-1;
		for i:=0 to cell^.sl-1 do
			inc(celkdel,tab1.sirsl[sl+i]+3);
		if length(ret)<celkdel then
			ret:=ret+' ';
		if length(ret)<celkdel then
			ret:=' '+ret;
		case cell^.hzar of
			-1:while length(ret)<celkdel do
				ret:=ret+' ';
			0:while length(ret)<celkdel do begin
				ret:=ret+' ';
				if length(ret)<celkdel then
					ret:=' '+ret;
			end;
			1:while length(ret)<celkdel do
				ret:=' '+ret;
		end;
	end else begin {oddelovac}
		celkdel:=-1;
		for i:=0 to cell^.sl-1 do
			inc(celkdel,tab1.sirsl[sl+i]+3);
		ret:='';
		for i:=1 to celkdel do
			ret:=ret+'-';
	end;
	write(kam,'|',ret);
	inc(sl,cell^.sl);
end;

procedure computetable4;
{nyni uz jenom rozstrka texty do pripravenych bunek}
var
	irad,isl,celkdel,i,j,x,y:integer;
begin
	celkdel:=1;
	for isl:=1 to tab1.pocsl do
		inc(celkdel,tab1.sirsl[isl]+3);
	write(kam,'+');			{prvni cara}
	for i:=2 to celkdel-1 do write(kam,'-');
	writeln(kam,'+');
	for irad:=1 to tab1.pocrad do begin
		for y:=1 to tab1.vysrad[irad] do begin
		{vsechny podradky}
			x:=1;
			while (x<=tab1.pocsl) do begin
				vypisbunku(irad,y,x,1);
				{vypise az nekolik sloupcu s pocatecnim |}
			end;
			writeln(kam,'|');
flush(kam);
		end;
		if irad<tab1.pocrad then begin
			x:=1;
			while (x<=tab1.pocsl) do begin
{todo! at to nedela na konci +}
				vypisbunku(irad,tab1.vysrad[irad]+1,x,2);
				{vypise az nekolik sloupcu s pocatecnim |}
			end;
			writeln(kam,'|');
		end;
	end;
	write(kam,'+');			{prvni cara}
	for i:=2 to celkdel-1 do write(kam,'-');
	writeln(kam,'+');
end;

begin
	getparams;
	readinput;
	computetable1;
	computetable2;
	computetable3;
	computetable4;
	closeall;
end.
