#include "krcd.h"

/* Parses something like "left-header|leftmid|center|rightmid|right-header" */
/*  and figures how long it is and how many gaps there are between sub headers */
static int krcdCalcHeaderWidths(char *header,int *gaps,enum style style,xmlDocPtr doc) {
    char *start, *pt;
    int cnt, width;

    if ( header==NULL ) {
	*gaps  = 0;
return( 0 );
    } else {
	width = 0; cnt = 0;
	for ( start=pt=header; *pt; ++pt ) {
	    if ( *pt=='|' ) {
		width += krcdTextWidth(start,pt-start,style,doc);
		++cnt;
		start = pt+1;
	    }
	}
	width += krcdTextWidth(start,pt-start,style,doc);
	/* note there is one fewer gap than there are header elements, so don't*/
	/*  increment cnt */
	*gaps  = cnt;
return( width );
    }
}

static void krcdBoxFigureWidths(KRCD_BOX *box, xmlDocPtr doc ) {
    int cnt, width, hw;
    KRCD_BOX *subbox;
    int i;

    box->header_width = krcdCalcHeaderWidths(box->header,&box->header_gaps,st_box_header,doc);
    box->topnotes_width = krcdCalcHeaderWidths(box->topnotes,&box->topnotes_gaps,st_box_notes,doc);
    box->bottomnotes_width = krcdCalcHeaderWidths(box->bottomnotes,&box->bottomnotes_gaps,st_box_notes,doc);

    box->meanwhile_subboxes = 1;
    if ( box->svg_description!=NULL ) {
	krcdSvgListBB(doc, box->svg_description, &box->bb );
/* printf( "Debug BB: %.1f,%.1f %.1f,%.1f\n", box->bb.minx, box->bb.miny, box->bb.maxx, box->bb.maxy ); */
    } else if ( box->boxes!=NULL ) {
	cnt = 0;
	for ( subbox = box->boxes; subbox!=NULL ; subbox=subbox->next ) {
	    krcdBoxFigureWidths(subbox,doc);
	    ++cnt;
	}
	box->meanwhile_subboxes = cnt;
	memcpy( &box->bb, &box->boxes->bb, sizeof( box->bb ));
	width = box->bb.maxx;
	for ( subbox = box->boxes->next; subbox!=NULL; subbox=subbox->next ) {
	    width += subbox->bb.maxx - subbox->bb.minx;
	    if ( box->bb.miny < subbox->bb.miny )
		box->bb.miny = subbox->bb.miny;
	    if ( box->bb.maxy > subbox->bb.maxy )
		box->bb.maxy = subbox->bb.maxy;
	}
	box->bb.maxx = width;
    } else {
	/* we want to center text so all the main commands line up */
	/*  that means we aren't interested in how far down the modifiers extend */
	/*  (if we center on the modifiers too then the main commands don't line up */
	box->bb.miny = 0;
	box->bb.maxy = krcdTextCapHeight(st_main);
	box->bb.minx = 0;
	width = 0;
	for ( i=0; i<4 && box->text[i]!=NULL; ++i )
	    width += krcdTextMainModWidth(box->text[i],box->modifier[i]);
	width += (i-1) * BETWEEN_TEXT_SPACING;
	box->bb.maxx = width;
    }
    width = box->bb.maxx - box->bb.minx;
    hw = box->header_width + box->header_gaps * BETWEEN_HEADER_SPACING_MIN;
    if ( hw>width )
	box->min_width = hw;
    else
	box->min_width = width;

    hw = box->topnotes_width + box->topnotes_gaps * BETWEEN_HEADER_SPACING_MIN;
    if ( hw>width )
	box->min_width = hw;
    hw = box->bottomnotes_width + box->bottomnotes_gaps * BETWEEN_HEADER_SPACING_MIN;
    if ( hw>width )
	box->min_width = hw;
}

static void krcdFigureWidths(KRCD *krcd) {
    KRCD_BOX *box;
    int bars = 0;

    for ( box = krcd->boxes; box!=NULL ; box=box->next ) {
	krcdBoxFigureWidths(box,krcd->doc);
	if ( box->boxes!=NULL && !box->meanwhile ) {
	    KRCD_BOX *subbox;
	    int subbars = bars;
	    for ( subbox = box->boxes; subbox!=NULL && subbox->next!=NULL; subbox=subbox->next ) {
		subbars += subbox->bars;
		if ( subbars%8==0 )
		    subbox->fullDivider = true;
		else if ( subbars%4==0 )
		    subbox->dashedDivider = true;
		else if ( subbars%2==0 )
		    subbox->dottedDivider = true;
	    }
	}
	bars += box->bars;
	if ( bars%8==0 )
	    box->fullDivider = true;
	else if ( bars%4==0 )
	    box->dashedDivider = true;
	else if ( bars%2==0 )
	    box->dottedDivider = true;
    }
    krcd->total_bars = bars;
}

#define MAX_LINES	10

static int krcdBreakLines(KRCD *krcd,int breaks[MAX_LINES],int bardiv ) {
    int tot_bars;
    int min_width;
    int lines;
    KRCD_BOX *box = krcd->boxes, *last_box;	/* last box which ends at a multiple of bardiv */
    int last_bars;

    tot_bars = 0;
    for ( lines = 0; lines<MAX_LINES && box!=NULL ; ++lines ) {
	min_width = 0;
	last_box = NULL;
	last_bars = 0;
	while ( box!=NULL && min_width < MAIN_WIDTH-4 ) {
	    min_width += box->min_width + 2 * BOX_MARGIN_MIN;
	    tot_bars += box->bars;
	    if ( min_width < MAIN_WIDTH-4 && tot_bars % bardiv==0 ) {
		last_box = box;
		last_bars = tot_bars;
	    }
	    if ( box->line_break_after )
	break;
	    box = box->next;
	}
	if ( last_box==NULL )
return( MAX_LINES+1 );		/* We have a single box that doesn't fit on a line (or 2 boxes in an 8 bar situation) */
	/* Note, if we reach the end of the boxes and everything fits */
	/*  then last_box will be set */
	breaks[lines] = last_bars;
	box = last_box->next;
	tot_bars = last_bars;
	if ( box==NULL )
return( lines+1 );
    }
    if ( krcd->boxes==NULL )
return( 0 );

return( MAX_LINES+1 );
}

static int krcdTest2Lines(KRCD *krcd,int l1bars ) {
    /* If we break the first bar at l1bars, will the second line fit */
    int tot_bars=0;
    int tot_min_width=0;
    KRCD_BOX *box = krcd->boxes;

    while ( box!=NULL && tot_min_width < MAIN_WIDTH-4 && tot_bars<l1bars ) {
	tot_min_width += box->min_width + 2 * BOX_MARGIN_MIN;
	tot_bars += box->bars;
	box = box->next;
    }
    if ( tot_bars!=l1bars )
return( 0 );		/* This might happen if we had a 16 bar box */

    tot_min_width = 0;
    while ( box!=NULL ) {
	tot_min_width += box->min_width + 2 * BOX_MARGIN_MIN;
	tot_bars += box->bars;
	box = box->next;
    }
return( tot_min_width<MAIN_WIDTH-4 );
}

static void krcdFigureLinePositions(KRCD *krcd, int lineCount, int *breaks ) {
    int lineWidths[MAX_LINES];
    int lineBoxCnt[MAX_LINES];
    int tot_bars=0, boxcnt;
    int tot_min_width=0;
    KRCD_BOX *box, *subbox, *lastbox;
    int line;
    int per_box, extra;
    int x,y, w, subx, oldx;

    for ( line=0, box = krcd->boxes; box != NULL; ++line ) {
	tot_min_width = 0;
	boxcnt = 0;
	while ( box!=NULL && tot_bars<breaks[line] ) {
	    if ( box->boxes!=NULL ) {
		for ( subbox = box->boxes; subbox!=NULL; subbox=subbox->next ) {
		    if ( subbox->min_width==subbox->bb.maxx-subbox->bb.minx )
			w = subbox->min_width + 2 * MEANWHILE_MARGIN_MIN;
		    else if ( subbox->bb.maxx-subbox->bb.minx + 2 * MEANWHILE_MARGIN_MIN > subbox->min_width + 2 * HEADER_MARGIN_MIN )
			w = subbox->bb.maxx-subbox->bb.minx + 2 * BOX_MARGIN_MIN;
		    else 
			w = subbox->min_width + 2 * HEADER_MARGIN_MIN;
		    tot_min_width += w;
		}
	    } else {
		if ( box->min_width==box->bb.maxx-box->bb.minx )
		    w = box->min_width + 2 * BOX_MARGIN_MIN;
		else if ( box->bb.maxx-box->bb.minx + 2 * BOX_MARGIN_MIN > box->min_width + 2 * HEADER_MARGIN_MIN )
		    w = box->bb.maxx-box->bb.minx + 2 * BOX_MARGIN_MIN;
		else 
		    w = box->min_width + 2 * HEADER_MARGIN_MIN;
		tot_min_width += w;
	    }

	    tot_bars += box->bars;
	    boxcnt += box->meanwhile_subboxes;
	    box = box->next;
	}
	lineWidths[line] = tot_min_width;
	lineBoxCnt[line] = boxcnt;
    }

    y = MAIN_HEADER_HEIGHT;
    tot_bars = 0;
    lastbox = NULL;
    for ( line=0, box = krcd->boxes; box != NULL; ++line ) {
	extra = MAIN_WIDTH-4 - lineWidths[line];
 if ( extra<0 )
  fprintf( stderr, "Compress too much: extra=%d\n", extra );
	per_box = extra / lineBoxCnt[line];
	extra -= per_box*lineBoxCnt[line];
	x = 2;
	if ( tot_bars%8==4 )
	    box->startOfLine4 = 1;
	while ( box!=NULL && tot_bars<breaks[line] ) {
	    box->actual_x = x;
	    box->actual_y = y;
	    if ( box->boxes!=NULL ) {
		oldx = subx = x;
		for ( subbox = box->boxes; subbox!=NULL; subbox=subbox->next ) {
		    if ( subbox->min_width==subbox->bb.maxx-subbox->bb.minx )
			w = subbox->min_width + 2 * MEANWHILE_MARGIN_MIN;
		    else if ( subbox->bb.maxx-subbox->bb.minx + 2 * MEANWHILE_MARGIN_MIN > subbox->min_width + 2 * HEADER_MARGIN_MIN )
			w = subbox->bb.maxx-subbox->bb.minx + 2 * BOX_MARGIN_MIN;
		    else 
			w = subbox->min_width + 2 * HEADER_MARGIN_MIN;
		    subbox->actual_width = w + per_box;
		    if ( extra>0 ) {
			++subbox->actual_width;
			--extra;
		    }
		    subbox->actual_x = subx;
		    subbox->actual_y = y;
		    subx += subbox->actual_width;
		}
		box->actual_width = subx-oldx;
	    } else {
		if ( box->min_width==box->bb.maxx-box->bb.minx )
		    w = box->min_width + 2 * BOX_MARGIN_MIN;
		else if ( box->bb.maxx-box->bb.minx + 2 * BOX_MARGIN_MIN > box->min_width + 2 * HEADER_MARGIN_MIN )
		    w = box->bb.maxx-box->bb.minx + 2 * BOX_MARGIN_MIN;
		else 
		    w = box->min_width + 2 * HEADER_MARGIN_MIN;
		box->actual_width = w + per_box;
		if ( extra>0 ) {
		    ++box->actual_width;
		    --extra;
		}
	    }
	    x += box->actual_width;
	    tot_bars += box->bars;
	    lastbox = box;
	    box = box->next;
	}
	if ( lastbox!=NULL )
	    lastbox->actual_width += 1;		/* This makes the final line be in the right place */
	if ( tot_bars%8==4 && box!=NULL )
	    lastbox->endOfLine4 = 1;
	y += MAIN_BOX_HEIGHT;
    }
}

/* the crib diagrom for Forty and County is laid out as three lines, 16, 16, 8 */
/* Fusilier's Dream as two lines: 24 and 16 */
/* The Library of Birmingham as 3 lines: 8, 20, 12 (there's a dotted line at end of second line and at begining of third) */
/* The Courage Reel as 2: 32, 16 */
/* Cadgers in the cannongate as 1: 48 */
/* A Trip O'er the Tweed as 2: 20, 28 */
/* Try to put as much stuff as possible on each line */
  /* try first breaking only at 8 bar breaks */
  /* then at 4 bar breaks (if the 4 bar break needs fewer lines, use it */
static void krcdFigurePositions(KRCD *krcd) {
    int breaks8[MAX_LINES], breaks4[MAX_LINES], *breaks = breaks8;
    int lc8, lc4, lc;
    int was4=0;

    lc = lc8 = krcdBreakLines(krcd,breaks8,8);
    if ( lc8>2 ) {
	lc4 = krcdBreakLines(krcd,breaks4,4);
	if ( lc4<lc8 ) {
	    was4 = 1;
	    breaks = breaks4;
	    lc = lc4;
	}
    }
    if ( lc>MAX_LINES ) {
	fprintf( stderr, "This dance is too complex for me to layout, I've run out of space.\n" );
exit( 1 );
    }

    /* Now suppose we have a 32 bar thing which can't quite fit in 32. We'll */
    /*  end up displaying it in 24, and 8. But it will usually look better */
    /*  as 16,16 */
    /* But note, if we have 8 and 24, then we already know we can't fit the first */
    /*  16 on one line */
    /* But I think it's better to have 24 & 16 than 20 and 20 for a 40 bar dance */
    /* But 28 and 12 should be replaced by 20 and 20 */
    if ( lc==2 && breaks[0]>breaks[1]-breaks[0] ) {
	int l1bars = breaks[0], l2bars = breaks[1]-breaks[0];
	int jump = was4 ? 4 : 8;

	while ( l1bars-jump >= l2bars+jump && krcdTest2Lines(krcd,l1bars-jump))
	    l1bars -= jump;
	breaks[0] = l1bars;
    }
    krcd->actual_line_cnt = lc;
	
    krcdFigureLinePositions(krcd, lc, breaks );
}

void krcdLayout(KRCD *krcd) {
    krcdFigureWidths(krcd);
    krcdFigurePositions(krcd);
}

/* ************************************************************************** */

static void krcdMassageAndOutputText(FILE *svgout,int indent, int x, int y,
	enum style style, xmlDocPtr doc, char *str ) {
    xmlNodePtr g;

    g = krcdMassageString( (xmlChar *) str, style, false, x, y, 0 );
    krcdOutputNode(svgout, indent, doc, g, false);
    xmlFreeNodeList(g);
}

static void krcd2SvgHeaderLayout(FILE *svgout,int indent,
	KRCD *krcd, KRCD_BOX *box, char *header, int gaps,
	int y, enum style style ) {
    int i, ch;
    int x;
    char *pt, *start;
    int width;

    if ( header==NULL )
return;

    if ( gaps==0 ) {
	/* center it */
	width = krcdTextWidth(header, -1, style, krcd->doc );
	krcdMassageAndOutputText(svgout, indent, box->actual_x+box->actual_width/2-width/2, y,
		style, krcd->doc, header );
    } else {
	pt = start = header;
	for ( i=0; i<=gaps; ++i ) {
	    start = pt;
	    while ( *pt!='|' && *pt!='\0' )
		++pt;
	    ch = *pt; *pt ='\0';
	    width = krcdTextWidth(start, -1, style, krcd->doc );
	    if ( i==0 )
		x = 8;
	    else if ( i==gaps )
		x = box->actual_width-8-width;
	    else
		x = i*box->actual_width/gaps - width/2;
	    krcdMassageAndOutputText(svgout, indent, box->actual_x+x, y,
		    style, krcd->doc, start );
	    *pt = ch;
	    if ( ch=='|' )
		++pt;
	}
    }
}

static void krcd2SvgBox(FILE *svgout,KRCD *krcd, KRCD_BOX *box,int indent ) {
    krcd2SvgHeaderLayout(svgout,indent,krcd, box,box->header,box->header_gaps, box->actual_y+32, st_box_header );
    if ( box->sequence || box->meanwhile )
	krcd2SvgHeaderLayout(svgout,indent,krcd, box,box->topnotes,box->topnotes_gaps, box->actual_y+24, st_box_notes );
    else
	krcd2SvgHeaderLayout(svgout,indent,krcd, box,box->topnotes,box->topnotes_gaps, box->actual_y+16, st_box_notes );
    krcd2SvgHeaderLayout(svgout,indent,krcd, box,box->bottomnotes,box->bottomnotes_gaps, box->actual_y+MAIN_BOX_HEIGHT-5, st_box_notes );
    krcd2SvgHeaderLayout(svgout,indent,krcd, box,box->bottomnotes2,box->bottomnotes_gaps, box->actual_y+MAIN_BOX_HEIGHT-20, st_box_notes );

    if ( box->boxes!=NULL ) {
	KRCD_BOX *subbox;
	/* draw the brackets */
	if ( box->meanwhile ) {
	    krcdOutputIndent(svgout,indent);
	    fprintf( svgout, "<path class=\"line\" d=\"M%d %dq0 -5 5 -5h%dq5 0 5 -5q0 5 5 5h%dq5 0 5 5\"/>\n",
		    box->actual_x+5, box->actual_y+10,
		    (box->actual_width-30)/2, box->actual_width-30-(box->actual_width-30)/2 );
	} else if ( box->sequence ) {
	    krcdOutputIndent(svgout,indent);
	    fprintf( svgout, "<path class=\"line\" d=\"M%d %dv-5 h%dv5\"/>\n",
		    box->actual_x+5, box->actual_y+10,
		    (box->actual_width-10) );
	}
	for ( subbox = box->boxes; subbox!=NULL; subbox = subbox->next )
	    krcd2SvgBox(svgout,krcd, subbox,indent+2 );
    } else if ( box->svg_description!=NULL ) {
	xmlNodePtr node;
	krcdOutputIndent(svgout,indent);
	fprintf( svgout, "<g transform=\"translate(%.0f,%.0f)\">\n",
	    box->actual_x+box->actual_width/2 - (box->bb.maxx-box->bb.minx)/2 - box->bb.minx,
	    box->actual_y+MAIN_BOX_HEIGHT/2 - (box->bb.maxy-box->bb.miny)/2 - box->bb.miny );
	for ( node=box->svg_description; node!=NULL; node=node->next ) {
	    krcdOutputNode(svgout,indent+4, krcd->doc, node, true);
	}
	krcdOutputIndent(svgout,indent);
	fprintf( svgout, "</g>\n" );
    } else {
	int x = box->actual_x + (box->actual_width - (box->bb.maxx-box->bb.minx))/2;
	int halfy = box->actual_y + MAIN_BOX_HEIGHT/2;
	int mainy = halfy + krcdTextCapHeight(st_main)/2;
	int mody = mainy + krcdTextCapHeight(st_modifier)/2;
	int i;
	int xstart;

	for ( i=0; i<4 && box->text[i]!=NULL; ++i ) {
	    krcdMassageAndOutputText(svgout, indent, x, mainy,
		    st_main, krcd->doc, box->text[i] );
	    xstart = x;
	    x += krcdTextWidth(box->text[i],-1,st_main, krcd->doc);
	    if ( box->half[i] ) {
		krcdOutputIndent(svgout,indent);
		fprintf( svgout, "<line x1=\"%d\" x2=\"%d\" y1=\"%d\" y2=\"%d\" class=\"line\" />\n",
			xstart-3, x+3, halfy, halfy );
	    }
	    if ( box->modifier[i]!=NULL ) {
		x += krcdTextMainModKern(box->text[i]);
		krcdMassageAndOutputText(svgout, indent, x, mody,
			st_modifier, krcd->doc, box->modifier[i] );
		x += krcdTextWidth(box->modifier[i],-1,st_modifier, krcd->doc);
	    }
	    x += BETWEEN_TEXT_SPACING;
	}
    }
    if (( box->fullDivider || box->dashedDivider || box->dottedDivider ) && box->drawDivider ) {
	krcdOutputIndent(svgout,indent);
	fprintf( svgout, "<line x1=\"%d\" x2=\"%d\" y1=\"%d\" y2=\"%d\" class=\"%s\" />\n",
		box->actual_x+box->actual_width, box->actual_x+box->actual_width,
		box->actual_y, box->actual_y+MAIN_BOX_HEIGHT,
		    box->fullDivider ? "line" :
		    box->dashedDivider ? "dashed" :
		      "dotted" );
    }
    if ( box->startOfLine4 ) {
	krcdOutputIndent(svgout,indent);
	fprintf( svgout, "<line x1=\"4\" x2=\"4\" y1=\"%d\" y2=\"%d\" class=\"dashed\" />\n",
		box->actual_y, box->actual_y+MAIN_BOX_HEIGHT );
    }
    if ( box->endOfLine4 ) {
	krcdOutputIndent(svgout,indent);
	fprintf( svgout, "<line x1=\"%d\" x2=\"%d\" y1=\"%d\" y2=\"%d\" class=\"dashed\" />\n",
		MAIN_WIDTH-4, MAIN_WIDTH-4,
		box->actual_y, box->actual_y+MAIN_BOX_HEIGHT );
    }
}

void krcd2Svg(KRCD *krcd, FILE *svgout, char *svgfilename, int indent, int inline_styles ) {
    /* Two cases: stand-alone svg file (supply filename), embedable in html (supply file arg) */
    int i;
    KRCD_BOX *box;
    int opened=0;

    if ( svgout==NULL ) {
	svgout = fopen( svgfilename, "w" );
	opened = 1;
	if ( svgout==NULL ) {
	    fprintf( stderr, "Failed to open %s for writing.\n", svgfilename );
return;
	}
	fprintf( svgout, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
	if ( !inline_styles )
	    fprintf( svgout, "<?xml-stylesheet type=\"text/css\" href=\"https://www.upadouble.info/css/krcd.css\" ?>\n" );
	fprintf( svgout, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 %d %d\">\n", MAIN_WIDTH, MAIN_HEADER_HEIGHT + krcd->actual_line_cnt*MAIN_BOX_HEIGHT + MAIN_FOOTER_HEIGHT );
	indent = 0;
    } else {
	if ( !inline_styles ) {
	    krcdOutputIndent(svgout,indent);
	    fprintf( svgout, "<?xml-stylesheet type=\"text/css\" href=\"https://www.upadouble.info/css/krcd.css\" ?>\n" );
	}
	krcdOutputIndent(svgout,indent);
	fprintf( svgout, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 %d %d\" style=\"width: 80%%; height: auto; margin-left: 10%%;\" >\n", MAIN_WIDTH, MAIN_HEADER_HEIGHT + krcd->actual_line_cnt*MAIN_BOX_HEIGHT + MAIN_FOOTER_HEIGHT );
    }
    if ( inline_styles ) {
	krcdOutputIndent(svgout,indent+2);
	fprintf( svgout, "<defs>\n" );
	krcdOutputIndent(svgout,indent+4);
	fprintf( svgout, "<style type=\"text/css\">\n" );
	krcdLibraryOutputStyles(svgout);
	krcdOutputIndent(svgout,indent+4);
	fprintf( svgout, "</style>\n" );
	krcdOutputIndent(svgout,indent+2);
	fprintf( svgout, "</defs>\n" );
    }
/*    else
	fprintf( svgout, "  <link rel=\"stylesheet\" href=\"https://www.upadouble.info/css/krcd.css\" type=\"text/css\"/>\n" ); */

    if ( krcd->last_mod_time!=0 ) {
	struct tm tm_creat, tm_mod;
	static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec" };
	krcdOutputIndent(svgout,indent+2);
	localtime_r(&krcd->last_mod_time,&tm_mod);
	if ( krcd->creation_time!=0 && krcd->creation_time!=krcd->last_mod_time ) {
	    localtime_r(&krcd->creation_time,&tm_creat);
	    fprintf( svgout, "<!-- source created on %d/%s/%d and last modified on %d/%s/%d. -->\n",
		tm_creat.tm_mday, months[tm_creat.tm_mon], tm_creat.tm_year+1900, 
		tm_mod.tm_mday, months[tm_mod.tm_mon], tm_mod.tm_year+1900 );
	} else
	    fprintf( svgout, "<!-- source last modified on %d/%s/%d. -->\n",
		tm_mod.tm_mday, months[tm_mod.tm_mon], tm_mod.tm_year+1900 );
    }
    krcdOutputIndent(svgout,indent+2);
    fprintf( svgout, "<rect fill=\"white\" x=\"-2\" y=\"-2\" width=\"%d\" height=\"%d\" />\n",
	    MAIN_WIDTH+4, MAIN_HEADER_HEIGHT+MAIN_FOOTER_HEIGHT + krcd->actual_line_cnt*MAIN_BOX_HEIGHT+4 );
    krcdOutputIndent(svgout,indent+2);
    fprintf( svgout, "<rect x=\"1\" width=\"%d\" y=\"1\" height=\"%d\" class=\"line\" />\n", MAIN_WIDTH-2, MAIN_HEADER_HEIGHT + krcd->actual_line_cnt*MAIN_BOX_HEIGHT );
    for ( i=0; i<krcd->actual_line_cnt; ++i ) {
	krcdOutputIndent(svgout,indent+2);
	fprintf( svgout, "<line x1=\"1\" x2=\"%d\" y1=\"%d\" y2=\"%d\" class=\"line\" />\n",
	MAIN_WIDTH-2, MAIN_HEADER_HEIGHT + i*MAIN_BOX_HEIGHT, MAIN_HEADER_HEIGHT + i*MAIN_BOX_HEIGHT );
    }
    if ( krcd->header_left != NULL ) {
	krcdMassageAndOutputText(svgout, indent+2, 15, 50,
		st_main, krcd->doc, krcd->header_left );
    }
    if ( krcd->header_right != NULL ) {
	krcdMassageAndOutputText(svgout, indent+2, MAIN_WIDTH-15-krcdTextWidth(krcd->header_right,-1,st_main,krcd->doc), 50,
		st_main, krcd->doc, krcd->header_right );
    }
    if ( krcd->footer_left != NULL ) {
	krcdMassageAndOutputText(svgout, indent+2, 5, MAIN_HEADER_HEIGHT + krcd->actual_line_cnt*MAIN_BOX_HEIGHT+MAIN_FOOTER_HEIGHT-10,
		st_footer, krcd->doc, krcd->footer_left );
    }
    if ( krcd->footer_right != NULL ) {
	krcdMassageAndOutputText(svgout, indent+2,
		MAIN_WIDTH-5-krcdTextWidth(krcd->footer_right,-1,st_footer, krcd->doc),
		MAIN_HEADER_HEIGHT + krcd->actual_line_cnt*MAIN_BOX_HEIGHT+MAIN_FOOTER_HEIGHT-10,
		st_footer, krcd->doc, krcd->footer_right );
    }

    for ( box = krcd->boxes; box!=NULL; box=box->next )
	krcd2SvgBox(svgout,krcd,box,indent+2);

    krcdOutputIndent(svgout,indent);
    fprintf( svgout, "</svg>\n" );

    if ( opened )
	fclose( svgout );
}
