// aplReport.cpp : Defines the initialization routines for the DLL.
//
#include "stdafx.h"

#define APL_REPORT_IMPL

#include "aplReport.h"
#include <aplXML.h>
#include <aplPdf.h>
#include <apl_image.h>
#include <apl_gui.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


int CaplReport::m_font_reduce_percent=30;

bool CaplReport::RotateBitmap(CBitmap &bmp, int angle)
{
	if(0==angle) return true;
	if(!((90==angle)|| (180==angle)||(270==angle)))return false;

	BITMAP BitMap;
	bmp.GetBitmap(&BitMap);
	int cx=BitMap.bmWidth;
	int cy=BitMap.bmHeight;
	if(0==cx || 0==cy) return false;

	CSize sz=bmp.GetBitmapDimension();

	int buflen=BitMap.bmHeight*BitMap.bmWidthBytes;
	if(0==buflen) return false;
	int buflen1=buflen;

	int strsz=BitMap.bmWidthBytes;
	int strsz1=strsz;

	char *buf=new char[buflen];
	bmp.GetBitmapBits(buflen,buf);

	char *buf1;
	if(180==angle) buf1=new char[buflen];
	else 
	{
		bmp.DeleteObject();
		if(!bmp.CreateBitmap(cy,cx,1,BitMap.bmBitsPixel,0))
		{
			CString msg;
			msg.Format(APL_T("Недостаточно ресурсов для создания изображения для поворота (%i x %i x %ibit)!\n"),cy,cx,BitMap.bmBitsPixel);
			msg+=APL_T("\n(Если ошибка возникла при печати - исправьте ориентацию страницы в настройках печати)");
			AfxMessageBox(msg,MB_ICONSTOP+MB_OK);
			delete buf;
			return false;
		}
		BITMAP BitMap1;
		bmp.GetBitmap(&BitMap1);
		buflen1=BitMap1.bmHeight*BitMap1.bmWidthBytes;
		buf1=new char[buflen1];
		strsz1=BitMap1.bmWidthBytes;
	}
	 if(1==BitMap.bmBitsPixel) memset(buf1,0,buflen1);

	int x,y,x1,y1;
	for(x=0;x<cx;x++)
	{
		for(y=0;y<cy;y++)
		{
			if(180==angle) {x1=cx-(x+1); y1=cy-(y+1);}
			else if(90==angle)  {x1=cy-(y+1); y1=x;}
			if(270==angle) {x1=y; y1=cx-(x+1);}

			//DWORD pixel=GetPixel32(buf,x,y,strsz);
			//SetPixel32(buf1,x1,y1,strsz1,pixel);

			if(32==BitMap.bmBitsPixel)
			{
				int offset=y*strsz + x*4;
				int offset1=y1*strsz1 + x1*4;
				//DWORD pixel= *((DWORD*)(buf+offset));
				//*((DWORD*)(buf1+offset1))=pixel;
				*((DWORD*)(buf1+offset1))=*((DWORD*)(buf+offset));
			}
			else if(16==BitMap.bmBitsPixel)
			{
				int offset=y*strsz + x*2;
				int offset1=y1*strsz1 + x1*2;
				*((WORD*)(buf1+offset1))=*((WORD*)(buf+offset));
			}
			else if(24==BitMap.bmBitsPixel)
			{
				int offset=y*strsz + x*3;
				int offset1=y1*strsz1 + x1*3;
				*((WORD*)(buf1+offset1))=*((WORD*)(buf+offset));
				*((BYTE*)(buf1+offset1+2))=*((BYTE*)(buf+offset+2));
			}
			else if(8==BitMap.bmBitsPixel) // не проверял
			{
				int offset=y*strsz + x;
				int offset1=y1*strsz1 + x1;
				*((BYTE*)(buf1+offset1))=*((BYTE*)(buf+offset));
			}
			else if(1==BitMap.bmBitsPixel) // не проверял
			{
				int a=(int)(x/8); // кол-во байт в строке до пиксела
				int b=x-(a*8);  // оставшее кол-во бит;
				int a1=(int)(x1/8); // кол-во байт в строке до пиксела
				int b1=x1-(a1*8);  // оставшее кол-во бит;

				int offset=y*strsz + a;
				int offset1=y1*strsz1 + a1;

				BYTE c=*((BYTE*)(buf+offset));
				BYTE c1=*((BYTE*)(buf1+offset1));
				c=c>>(7-b);
				c&=1;
				c=c<<(7-b1);
				c1=c1|=c;
				*((BYTE*)(buf1+offset1))=c1;
				int aaa=0;
			}
			else
			{
				AfxMessageBox(APL_T("Неизвестный способ кодирования цветов (RotateBitmap)"));
				x=cx;
				break;
			}
		}
	}
	bmp.SetBitmapBits(buflen1,buf1);

	delete buf;
	delete buf1;
	return true;
}


CaplReport::CaplReport(void)
{
	m_bIsTemplate=false;
}


CaplReport::~CaplReport(void)
{
	Clear();
}

void CaplReport::Clear()
{
	m_fonts.Clear();
	m_pens.Clear();
	m_pages.Clear();
	m_images.Clear();
}
//**********************************************************************************************
//**********************************************************************************************
//**********************************************************************************************
//**********************************************************************************************

TCHAR hexchar2char(TCHAR c)
{
	if(c>=_T('0') && c<=_T('9')) return c-_T('0');
	if(c>=_T('A') && c<=_T('F')) return c+10-_T('A');
	if(c>=_T('a') && c<=_T('f')) return c+10-_T('a');
	return _T('\0');
}


bool CaplReport::OpenFile(LPCTSTR report_file)
{
	//return OpenXML(report_file,true);
	if(0==report_file) return false;
	if(report_file[0]==_T('\0')) return false;

	CFile file;
	if(!file.Open(report_file,CFile::modeRead)) return false;

	ULONGLONG flen=file.GetLength();
	if(flen>0x80000000) return false; // Файлы более 2Гб не поддерживаются
	UINT iLen=(UINT)flen;

	BYTE *srcbuf=new BYTE[iLen+1];
	if(0==srcbuf) return false;
	if(!file.Read(srcbuf,iLen)) {delete srcbuf;	return false; }
	srcbuf[iLen]='\0';
	file.Close();

	bool res= OpenXML((LPCTSTR)srcbuf,iLen,false);
	delete srcbuf;
	return res;
}

bool CaplReport::OpenFileFromMemory(unsigned char *mem, ULONG memsize) // Открыть файл из памяти 
{
	return OpenXML((LPCTSTR)mem,memsize,false);
}

bool CaplReport::OpenXML(LPCTSTR data_buf, UINT data_len, bool bFromFile)
{
	Clear();
	CaplXMLFile xml_report_file;

	if(bFromFile)
	{
		if(!xml_report_file.LoadFromFile(data_buf)) {return false;}
	}
	else
	{
		LPCTSTR xmlbuf=data_buf;
		CaplZip zip;
		if(zip.IsBufferAplZip((BYTE*)data_buf,data_len))
		{
			zip.Decompress((BYTE*)data_buf,data_len);
			xmlbuf=(LPCTSTR)zip.GetData();
		}
		if(!xml_report_file.LoadFromBuffer(xmlbuf)) {return false;}
	}

	int i;
	CString buf,buf1;
	CString sErrors;
	CaplStrMap m_font_map;
	CaplStrMap m_pen_map;

	m_bIsTemplate=false;
	if(xml_report_file.root.subnodes.GetSize()<1){AfxMessageBox(APL_T("Некорректный XML!"), MB_ICONSTOP); return false;}
	if(xml_report_file.root.subnodes[0]->name==_T("apl_report_template")) m_bIsTemplate=true;
	else if(xml_report_file.root.subnodes[0]->name!=APL_T("apl_report")){AfxMessageBox(APL_T("Неизвестная схема XML!"), MB_ICONSTOP); return false;}

	// Читаем шрифты
	APL_XML_NODES nodes, fields_nodes, table_nodes;
	xml_report_file.root.FindNodesBN(_T("font"),nodes);
	for(i=0; i<nodes.GetSize();i++)
	{
		CaplXMLNode *node=nodes[i];
		CaplReportFont *font=new CaplReportFont();

		node->GetParam(_T("id"),font->m_id);
		node->GetParam(_T("name"),font->m_name);
		node->GetParam(_T("size"),font->m_size);
		node->GetParam(_T("bold"),font->m_bBold);
		node->GetParam(_T("underline"),font->m_bUnderline);
		node->GetParam(_T("italic"),font->m_bItalic);

		if(m_font_map.Find(font->m_id)>=0)
		{
			buf.Format(APL_T("Проигнорирован дублирующийся шрифт! <font id=\"%s\">\n"),LPCTSTR(font->m_id));
			sErrors+=buf;
			//AfxMessageBox(buf,MB_OK|MB_ICONSTOP);
			delete font;
		}
		else
		{
			int index=m_fonts.Add(font);
			m_font_map.Add(font->m_id,index);
		}
	}

	// Читаем pens
	xml_report_file.root.FindNodesBN(_T("pen"),nodes);
	for(i=0; i<nodes.GetSize();i++)
	{
		CaplXMLNode *node=nodes[i];
		CaplReportPen *pen=new CaplReportPen();

		node->GetParam(_T("id"),pen->m_id);
		node->GetParam(_T("width"),pen->m_width);
		node->GetParam(_T("color"),buf);
		if(buf[0]==_T('#'))
		{
			unsigned char r=(hexchar2char(buf[1])<<4) | (hexchar2char(buf[2]));
			unsigned char g=(hexchar2char(buf[3])<<4) | (hexchar2char(buf[4]));
			unsigned char b=(hexchar2char(buf[5])<<4) | (hexchar2char(buf[6]));
			pen->m_color=RGB(r,g,b);
		}
		else  pen->m_color=_atoi(buf);

		if(m_pen_map.Find(pen->m_id)>=0)
		{
			buf.Format(APL_T("Проигнорирован дублирующийся карандаш! <pen id=\"%s\">\n"),LPCTSTR(pen->m_id));
			sErrors+=buf;
			//AfxMessageBox(buf,MB_OK|MB_ICONSTOP);
			delete pen;
		}
		else
		{
			int index=m_pens.Add(pen);
			m_pen_map.Add(pen->m_id,index);
		}
	}

	// Читаем картинки
	CaplStrMap map_images;
	xml_report_file.root.FindNodesBN(_T("image"),nodes);
	for(i=0; i<nodes.GetSize();i++)
	{
		CaplXMLNode *node=nodes[i];
		CaplReportImage *image=new CaplReportImage();
		node->GetParam(_T("id"),image->m_id);
		node->GetParam(_T("name"),image->m_name);

		unsigned int image_len=0;
		if(CaplXMLFile::DecodeFromBase64(LPCTSTR(node->text),0,image_len))
		{
			if(image_len>0)
			{
				image->m_blob=new unsigned char[image_len];
				CaplXMLFile::DecodeFromBase64(LPCTSTR(node->text),image->m_blob,image_len);
				image->m_blob_size=image_len;
			}
		}
		m_images.Add(image);
		map_images.Add(image->m_id,image);
	}

	// Читаем страницы
	xml_report_file.root.FindNodesBN(_T("page"),nodes);
	for(i=0; i<nodes.GetSize();i++)
	{
		CaplXMLNode *node=nodes[i];
		CaplReportPage *page=new CaplReportPage(this);
		//node->GetParam(_T("id"),pen->m_id);
		node->GetParam(_T("name"),page->m_name);
		node->GetParam(_T("cx"),page->m_cx_mm);
		node->GetParam(_T("cy"),page->m_cy_mm);
		int index=m_pages.Add(page);
		page->m_num=index;

		// Читаем поля
		node->FindNodesBN(_T("field"),fields_nodes);
		int j;
		for(j=0;j<fields_nodes.GetSize();j++)
		{
			CaplXMLNode *field_node=fields_nodes[j];
			CaplReportField *field=new CaplReportField;

			//field_node->GetParam(_T("name"),page->m_name);
			field_node->GetParam(_T("x"),field->m_x);
			field_node->GetParam(_T("y"),field->m_y);
			field_node->GetParam(_T("cx"),field->m_cx);
			field_node->GetParam(_T("cy"),field->m_cy);
			field_node->GetParam(_T("ident-h"),field->m_ident_x);
			field_node->GetParam(_T("ident-v"),field->m_ident_y);
			field_node->GetParam(_T("field"),field->m_field);
			field_node->GetParam(_T("text"),field->m_text);
			field_node->GetParam(_T("data"),field->m_data);

			field_node->GetParam(_T("image"),buf);
			if(buf!=_T("")) 
				field->m_image=(CaplReportImage*)map_images.GetP(buf);

			field_node->GetParam(_T("font"),buf);
			if(buf!=_T(""))
			{
				int index=m_font_map.Find(buf);
				if(index>=0) field->m_font_index=(m_font_map.GetAt(index))->val;
				else
				{
					buf1.Format(APL_T("Ссылка на неизвестный шрифт!  (font id=\"%s\")\n"),LPCTSTR(buf));
					sErrors+=buf1;
				}
			}
			field_node->GetParam(_T("align"),buf); if(buf!=_T("")) field->m_h_allign=buf[0];
			field_node->GetParam(_T("valign"),buf); if(buf!=_T("")) field->m_v_allign=buf[0];

			field_node->GetParam(_T("pen"),buf);
			if(buf!=_T(""))
			{
				int pen_index=m_pen_map.Find(buf);
				if(pen_index>=0) field->m_pen_index=(m_pen_map.GetAt(pen_index))->val;
				else
				{
					buf1.Format(APL_T("Ссылка на неизвестный карандаш! (pen id=\"%s\")>\n"),LPCTSTR(buf));
					sErrors+=buf1;
				}
			}

			field_node->GetParam(_T("top_pen"),buf);
			if(buf!=_T(""))
			{
				int pen_index=m_pen_map.Find(buf);
				if(pen_index>=0) field->m_top_pen_index=(m_pen_map.GetAt(pen_index))->val;
				else
				{
					buf1.Format(APL_T("Ссылка на неизвестный карандаш! (pen id=\"%s\")>\n"),LPCTSTR(buf));
					sErrors+=buf1;
				}
			}
			field_node->GetParam(_T("angle"),buf); if(buf!=_T("")) field->m_angle=_atoi(LPCTSTR(buf));

			page->m_fields.Add(field);
		}

		//Читаем таблицу
		node->FindNodesBN(_T("table"),table_nodes);
		if(table_nodes.GetSize()>0)
		{
			CaplXMLNode *table_node=table_nodes[0];
			
			CaplReportTable *table=new CaplReportTable;

			table_node->GetParam(_T("id"),table->m_id);
			table_node->GetParam(_T("x"),table->m_x);
			table_node->GetParam(_T("y"),table->m_y);
			table_node->GetParam(_T("cx"),table->m_cx);
			table_node->GetParam(_T("cy"),table->m_cy);
			table_node->GetParam(_T("row-cy"),table->m_row_cy);

			table_node->GetParam(_T("pen"),buf);
			if(buf!=_T(""))
			{
				table->m_pen_id=buf;
				int pen_index=m_pen_map.Find(buf);
				if(pen_index>=0) table->m_pen_index=(m_pen_map.GetAt(pen_index))->val;
				else
				{
					buf1.Format(APL_T("Ссылка на неизвестный карандаш! (pen id=\"%s\")>\n"),LPCTSTR(buf));
					sErrors+=buf1;
				}
			}
			page->m_table=table;

			//читаем столбцы
			APL_XML_NODES colums_nodes;
			node->FindNodesBN(_T("column"),colums_nodes);
			int k;
			for(k=0;k<colums_nodes.GetSize();k++)
			{
				CaplXMLNode *column_node=colums_nodes[k];

				CaplReportTableColumn *column=new CaplReportTableColumn(table);

				column_node->GetParam(_T("id"),column->m_id);
				column_node->GetParam(_T("cx"),column->m_cx);
				column_node->GetParam(_T("ident-h"),column->m_ident_x);
				column_node->GetParam(_T("ident-v"),column->m_ident_y);

				column_node->GetParam(_T("font"),buf);
				if(buf!=_T(""))
				{
					column->m_font_id=buf;
					int index=m_font_map.Find(buf);
					if(index>=0)
					{
						column->m_font_index=(m_font_map.GetAt(index))->val;
						column->m_font_id=buf;
					}
					else
					{
						buf1.Format(APL_T("Ссылка на неизвестный шрифт!  (font id=\"%s\")\n"),LPCTSTR(buf));
						sErrors+=buf1;
					}
				}
				column_node->GetParam(_T("align"),buf); if(buf!=_T("")) column->m_h_allign=buf[0];
				column_node->GetParam(_T("valign"),buf); if(buf!=_T("")) column->m_v_allign=buf[0];

				page->m_table->m_columns.Add(column);
			}
		}
	}

	xml_report_file.root.FindNodesBN(_T("signtype"),nodes);
	for(i=0; i<nodes.GetSize();i++)
	{
		CaplXMLNode *node=nodes[i];
		CaplReportSignType *st=new CaplReportSignType();
		node->GetParam(_T("id"),st->m_id);
		node->GetParam(_T("name"),st->m_name);
		node->GetParam(_T("role"),st->m_role);
		node->GetParam(_T("status"),st->m_status);
		m_sign_types.Add(st);
	}
	

	if(sErrors!=_T("")) AfxMessageBox(sErrors,MB_ICONSTOP);
	//else AfxMessageBox(APL_T("Готово!"));
	if(m_pages.GetSize()>0) return true;
	return false;
	
}
//******************************************************************
//******************************************************************
//******************************************************************
bool CaplReport::SaveReport(LPCTSTR report_file, bool bNotSaveEmptyField, bool bCompress)
{
	if(0==report_file) return false;
	if(_T('0')==report_file[0]) return false;

	CString buf;
	CaplXMLFile xml_report_file;
	xml_report_file.xmlversion=_T("1.0");
	xml_report_file.filecharset=_T("windows-1251");
	xml_report_file.root.name=_T("apl_report");

	int i,j;
	// сохраняем шрифты
	for(i=0;i<m_fonts.GetSize();i++)
	{
		CaplReportFont *font=m_fonts[i];
		CaplXMLNode *font_node=new CaplXMLNode(_T("font"), &xml_report_file.root);
		font_node->SetParam(_T("id"),font->m_id,true);
		font_node->SetParam(_T("name"),font->m_name,true);
		font_node->SetParam(_T("size"),font->m_size,true);
		if(font->m_bBold) buf=_T("true"); else buf=_T("false");
		font_node->SetParam(_T("bold"),buf,true);
		if(font->m_bUnderline) buf=_T("true"); else buf=_T("false");
		font_node->SetParam(_T("underline"),buf,true);
		if(font->m_bItalic) buf=_T("true"); else buf=_T("false");
		font_node->SetParam(_T("italic"),buf,true);
	}

	// сохраняем карандаши
	for(i=0;i<m_pens.GetSize();i++)
	{
		CaplReportPen *pen=m_pens[i];
		CaplXMLNode *pen_node=new CaplXMLNode(_T("pen"), &xml_report_file.root);
		pen_node->SetParam(_T("id"),pen->m_id,true);
		pen_node->SetParam(_T("width"),pen->m_width,true);
		buf.Format(_T("#%02X%02X%02X"),GetRValue(pen->m_color),GetGValue(pen->m_color),GetBValue(pen->m_color));
		pen_node->SetParam(_T("color"),buf,true);
	}

	// Нумеруем картинки
	for(i=0;i<m_images.GetSize();i++) m_images[i]->m_id.Format(_T("%i"),i);

	// сохраняем поля
	for(i=0;i<m_pages.GetSize();i++)
	{
		CaplReportPage *page=m_pages[i];
		CaplXMLNode *page_node=new CaplXMLNode(_T("page"), &xml_report_file.root);
		page_node->SetParam(_T("cx"),page->m_cx_mm,true);
		page_node->SetParam(_T("cy"),page->m_cy_mm,true);
		for(j=0;j<page->m_fields.GetSize();j++)
		{
			CaplReportField *field=page->m_fields[j];
			if(0==field){AfxMessageBox(_T("field=0!!!")); continue;}

			if(bNotSaveEmptyField)
			{
				if(field->m_pen_index<0 && field->m_text==_T("") && field->m_field==_T("") && 0==field->m_image) continue;
			}

			CaplXMLNode *field_node=new CaplXMLNode(_T("field"), page_node);
			field_node->SetParam(_T("x"),field->m_x,true);
			field_node->SetParam(_T("y"),field->m_y,true);
			field_node->SetParam(_T("cx"),field->m_cx,true);
			field_node->SetParam(_T("cy"),field->m_cy,true);

			if(field->m_pen_index>=0)   
			{
				if(field->m_pen_index>=m_pens.GetSize())
				{
					CString msg;
					msg.Format(_T("incorrect m_pen_index\n\n m_pen_index=%i   m_pens.Size=%i"),field->m_pen_index,m_pens.GetSize());	
					AfxMessageBox(msg,MB_ICONSTOP|MB_OK);
				}
				else field_node->SetParam(_T("pen"),m_pens[field->m_pen_index]->m_id,true);
			}
			if(field->m_top_pen_index>=0)   
			{
				if(field->m_top_pen_index>=m_pens.GetSize())
				{
					CString msg;
					msg.Format(_T("incorrect m_top_pen_index\n\n m_top_pen_index=%i   m_pens.Size=%i"),field->m_top_pen_index,m_pens.GetSize());	
					AfxMessageBox(msg,MB_ICONSTOP|MB_OK);
				}
				else field_node->SetParam(_T("top_pen"),m_pens[field->m_top_pen_index]->m_id,true);
			}

			if(field->m_field!=_T("") || field->m_text!=_T(""))
			{
				if(0!=field->m_ident_x) field_node->SetParam(_T("ident-h"),field->m_ident_x,true);
				if(0!=field->m_ident_y) field_node->SetParam(_T("ident-v"),field->m_ident_y,true);

				if(field->m_text!=_T(""))field_node->SetParam(_T("text"),field->m_text,true);

				if(field->m_field!=_T("")) field_node->SetParam(_T("field"),field->m_field,true);
				if(field->m_font_index>=0) 	field_node->SetParam(_T("font"),m_fonts[field->m_font_index]->m_id,true);
			
				buf=field->m_h_allign;
				field_node->SetParam(_T("align"),buf,true);
				buf=field->m_v_allign;
				field_node->SetParam(_T("valign"),buf,true);

				if(field->m_angle!=0){buf.Format(_T("%i"), field->m_angle); field_node->SetParam(_T("angle"),buf,true);}
			}

			if(field->m_data!=_T(""))
				field_node->SetParam(_T("data"),field->m_data,true);

			if(0!=field->m_image) 
			{
				field_node->SetParam(_T("image"),field->m_image->m_id,true);

				buf=field->m_h_allign;
				field_node->SetParam(_T("align"),buf,true);
				buf=field->m_v_allign;
				field_node->SetParam(_T("valign"),buf,true);
			}
		}

		// сохраняем таблицу
		if(0!=page->m_table)
		{
			CaplReportTable *table=page->m_table;

			CaplXMLNode *table_node=new CaplXMLNode(_T("table"), page_node);
			if(table->m_id!=_T("")) table_node->SetParam(_T("id"),table->m_id);
			table_node->SetParam(_T("x"),table->m_x);
			table_node->SetParam(_T("y"),table->m_y);
			table_node->SetParam(_T("cx"),table->m_cx);
			table_node->SetParam(_T("cy"),table->m_cy);
			table_node->SetParam(_T("row-cy"),table->m_row_cy);
			table_node->SetParam(_T("pen"),table->m_pen_id);

			int k;
			for(k=0;k<table->m_columns.GetSize();k++)
			{
				CaplReportTableColumn *column=table->m_columns[k];
				CaplXMLNode *column_node=new CaplXMLNode(_T("column"), table_node);
				if(column->m_id!=_T("")) column_node->SetParam(_T("id"),column->m_id);
				column_node->SetParam(_T("cx"),column->m_cx);
				if(0!=column->m_ident_x) column_node->SetParam(_T("ident-h"),column->m_ident_x);
				if(0!=column->m_ident_y) column_node->SetParam(_T("ident-v"),column->m_ident_y);
				column_node->SetParam(_T("font"),column->m_font_id);
			}
		}
	}

	// Сохраняем картинки
	for(i=0;i<m_images.GetSize();i++)
	{
		CaplReportImage *image=m_images[i];
		CaplXMLNode *image_node=new CaplXMLNode(_T("image"), &xml_report_file.root);
		image_node->SetParam(_T("id"),image->m_id,true);
		image_node->SetParam(_T("name"),image->m_name,true);
		if(image->m_blob_size>0)
			CaplXMLFile::EncodeToBase64(image->m_blob,image->m_blob_size,image_node->text);
	}

	// Сохраняем настройки подписей
	for(i=0;i<m_sign_types.GetSize();i++)
	{
		CaplReportSignType *st=m_sign_types[i];
		CaplXMLNode *image_node=new CaplXMLNode(_T("signtype"), &xml_report_file.root);
		image_node->SetParam(_T("id"),st->m_id,true);
		image_node->SetParam(_T("status"),st->m_status,true);
		image_node->SetParam(_T("role"),st->m_role,true);
		if(st->m_name!=_T("")) image_node->SetParam(_T("name"),st->m_name,true);
	}

	CFileFind ff;
	if(ff.FindFile(report_file))
	{
		buf.Format(APL_T("Файл \"%s\" уже существует. Перезаписать?"),report_file);
		if(IDYES!=AfxMessageBox(buf,MB_YESNO)) return false;
		SetFileAttributes(report_file,FILE_ATTRIBUTE_NORMAL);
		DeleteFile(report_file);
	}

	if(!xml_report_file.SaveToFile(report_file)) return false;
	if(!bCompress) return  true;

	CaplZip zip;
	return zip.CompressFile(report_file);
}
//******************************************************************
CaplReportImage *CaplReport::addImageFromFile(LPCTSTR file_name)
{
	if(0==file_name) return 0;
	if(_T('0')==file_name[0]) return 0;
	CFile file;
	if(!file.Open(file_name,CFile::modeRead|CFile::shareDenyWrite)) return 0;
	int len=(int)file.GetLength();
	if(0==len) {file.Close(); return 0;}
	unsigned char *buf=new unsigned char[len];
	file.Read(buf,len);
	file.Close();

	CaplReportImage *img=new CaplReportImage();
	m_images.Add(img);
	img->m_blob=buf;
	img->m_blob_size=len;
	
	CString name=file_name;
	int i=name.ReverseFind(_T('\\'));
	if(i>=0)
	{
		img->m_name=name.Right(name.GetLength()-(i+1));
		img->m_name.MakeLower();
	}
	return img;
}
//******************************************************************
CaplReportImage *CaplReport::addImageFromMemory(LPCTSTR file_name, unsigned char *mem, long memsize)
{
	if(0==file_name || 0==mem || 0==memsize) return 0;
	if(_T('0')==file_name[0]) return 0;

	CaplReportImage *img=new CaplReportImage();
	m_images.Add(img);
	img->m_blob=mem;
	img->m_blob_size=memsize;

	CString name=file_name;
	int i=name.ReverseFind(_T('\\'));
	if(i>=0)
	{
		img->m_name=name.Right(name.GetLength()-(i+1));
		img->m_name.MakeLower();
	}
	return img;
}
//******************************************************************
//******************************************************************
//******************************************************************
//******************************************************************
bool CaplReport::SetParams(CaplStrStrMap *params_map, bool bReplaceText)
{
	if(0==params_map) return false;
	int i,j;
	for(i=0;i<m_pages.GetSize();i++)
	{
		CaplReportPage *page=m_pages[i];
		for(j=0;j<page->m_fields.GetSize();j++)
		{
			CaplReportField *field=page->m_fields[j];
			if(field->m_field==_T("")) continue;

			if((!bReplaceText) && field->m_text!=_T("")) continue;

			int k=params_map->FindByIn(field->m_field);
			if(k<0) continue;
			field->m_text=(params_map->GetAt(k))->val;
			//if(field->m_text!=_T("")) field->m_field=_T("");
		}
	}
	return true;
}
CaplReportField *CaplReport::FindFieldByName(LPCTSTR name)
{
	int i,j;
	for(i=0;i<m_pages.GetSize();i++)
	{
		CaplReportPage *page=m_pages[i];
		for(j=0;j<page->m_fields.GetSize();j++)
		{
			CaplReportField *field=page->m_fields[j];
			if(field->m_field==name) return field;
		}
	}
	return 0;
}
//******************************************************************
//******************************************************************
//******************************************************************
bool CaplReport::GenerateReport(CaplStrStrMap *params_map,  T_LIST_CaplReportDataRow *rows, LPCTSTR report_file,
								bool bCalcFieldHigh, bool bDublicateRows)
{
	if(0==report_file || _T('\0')==report_file[0]) return false;
	CString buf;
	if(0==m_pages.GetSize())
	{
		AfxMessageBox(APL_T("Ошибка: Шаблон не загружен!"),MB_ICONSTOP|MB_OK);
		return false;
	}

	int i,j;

	// Разолняем параметры (кроме таблицы и номера страниц) 
	// Делаем вначале, т.к. потом страниц будет больше
	SetParams(params_map,true);

	// заполняем данные
	int i_page=0; 
	if(0!=rows)
	{
		CaplStrMap map_fonts;	for(i=0;i<m_fonts.GetSize();i++) map_fonts.Add(m_fonts.GetAt(i)->m_id,i);
		CaplStrMap map_pens;    for(i=0;i<m_pens.GetSize();i++) map_pens.Add(m_pens.GetAt(i)->m_id,i);

		CFont *fonts=0;
		int n=m_fonts.GetSize();
		if(n>0)
		{
			fonts=new CFont[n];
			for(i=0;i<n;i++)
			{
				CaplReportFont *rfont=m_fonts[i];
				int nWeight= FW_NORMAL; if(rfont->m_bBold)  nWeight=FW_BOLD;
				BYTE Underline=0; if(rfont->m_bUnderline)Underline=1;
				BYTE Italic=0;  if(rfont->m_bItalic) Italic=1;
				int high=(int)(-rfont->m_size*10);  // считать будем в 0.1 мм
				fonts[i].CreateFont(high,0,0,0,nWeight,Italic,Underline,0,RUSSIAN_CHARSET,0,0,0,0,rfont->m_name);
			}
		}

		i_page=-1;  // текущая заполняемая страница
		int i_num_template_pages=m_pages.GetSize();

		float fx=0,fy=0,fcx=0,fcy=0; //размеры текущего поля
		float ty=0; //  текущая верхняя граница для добавления поля
		float row_cy=0; //  высота текущей строки

		CaplReportPage *curpage=0;

		HWND desktop_hwnd= GetDesktopWindow();
		CWnd desktop; desktop.Attach(desktop_hwnd);

		CDC tmp_dc, *desktopDc=desktop.GetDC();
		tmp_dc.CreateCompatibleDC(desktopDc);
		desktop.ReleaseDC(desktopDc);
		desktop.Detach();

		CaplReportDataRow *row4CopyToNextPage=0;

		for(i=0;i<rows->GetSize();i++)
		{
			CaplReportDataRow *cur_row=rows->GetAt(i);

			if(0==cur_row->m_cy) // Разрыв раздела
			{
				row4CopyToNextPage=0;
				continue;
			}

			if(0==curpage)
			{
				// Создаем копию последней страницы, если это надо
				i_page++;
				if(i_page==m_pages.GetSize()-1) 
					m_pages.Add(new CaplReportPage(m_pages[m_pages.GetSize()-1]));
				curpage=m_pages[i_page];
				//Ищем поле для вывода данных
				bool bFoundTable=false;
				for(j=0;j<curpage->m_fields.GetSize(); j++)
				{
					CaplReportField *feild=curpage->m_fields[j];
					if(feild->m_field==_T("#table"))
					{
						bFoundTable=true;
						fx=feild->m_x;
						fy=feild->m_y;
						fcx=feild->m_cx;
						fcy=feild->m_cy;
						ty=0;
						row_cy=0;
						break;
					}
				}
				if(!bFoundTable)
				{
					buf.Format(APL_T("На листе %i на найдено поле для вывода таблицы (#table)!"),i_page);
					AfxMessageBox(buf,MB_OK|MB_ICONWARNING);
					i--; curpage=0; 

					if((i_page+1)<i_num_template_pages)continue;
					Clear();
					AfxMessageBox(APL_T("Не удалось сфорировать очтет (не указаны поля для вывода данных (#table)"),MB_OK|MB_ICONSTOP);
					return false;
				} 

				// Делаем копию строки заголока (которую надо дублировать)
				if(!cur_row->m_bCopyToNextPage) // След. будет строка, которая сама заголовок
				{
					if(0!=row4CopyToNextPage && row4CopyToNextPage!=cur_row)
					{
						// Вставка строки заголовка
						CaplReportDataRow *new_row=new CaplReportDataRow;
						new_row->m_cy=row4CopyToNextPage->m_cy;
						new_row->m_top_indent=row4CopyToNextPage->m_top_indent;
						new_row->m_bKeepWithNext=row4CopyToNextPage->m_bKeepWithNext;
						new_row->m_top_line_pen=row4CopyToNextPage->m_top_line_pen;
						new_row->m_bCopyToNextPage=row4CopyToNextPage->m_bCopyToNextPage;

						int k;
						for(k=0;k<row4CopyToNextPage->m_data_fields.GetSize();k++)
						{
							CaplReportField *field_old=row4CopyToNextPage->m_data_fields[k];
							CaplReportField *feld_new=new CaplReportField();
							new_row->m_data_fields.Add(feld_new);

							feld_new->m_text=field_old->m_text;
							feld_new->m_cx=field_old->m_cx;
							feld_new->m_pen_id=field_old->m_pen_id;
							feld_new->m_font_id=field_old->m_font_id;
							feld_new->m_h_allign=field_old->m_h_allign;
							feld_new->m_v_allign=field_old->m_v_allign;
							feld_new->m_data=field_old->m_data;
						}
						rows->Insert(i,new_row);
						cur_row=new_row;
					}
				}
			}

			// Создаем строку полей и Расчитываем ширину столбцов

			int n_witn_no_cx=0; // Кол-во столбцов c незаданной шириной
			float r_cx=0; 

			if(0!=ty)
				ty+=cur_row->m_top_indent;

			int row_top_line_index=-1;
			if(cur_row->m_top_line_pen!=_T("")) row_top_line_index=	map_pens.Get(cur_row->m_top_line_pen);

			for(j=0;j<cur_row->m_data_fields.GetSize();j++)
			{
				CaplReportField *data_field=cur_row->m_data_fields[j];
				CaplReportField *field=new CaplReportField(data_field);
				cur_row->m_report_fields.Add(field);
				if(0!=field->m_cx) r_cx+=field->m_cx;
				else n_witn_no_cx++;

				field->m_top_pen_id=cur_row->m_top_line_pen;

				field->m_font_index=map_fonts.Get(data_field->m_font_id);
				if(data_field->m_pen_id!=_T("")) field->m_pen_index=map_pens.Get(data_field->m_pen_id);
				field->m_top_pen_index=row_top_line_index;
			}

			// Определяем ширину для не заданных (делим поровну) и прочие координаты
			float cx4Empty=0; 
			if(0!=n_witn_no_cx) 
				cx4Empty=(fcx-r_cx)/n_witn_no_cx;

			// Записываем координаты по горизонтали
			float m_cur_x=fx;
			for(j=0;j<cur_row->m_report_fields.GetSize();j++)
			{
				CaplReportField *field=cur_row->m_report_fields[j];
				if(0==field->m_cx) field->m_cx=cx4Empty;
				field->m_x=m_cur_x;
				m_cur_x+=field->m_cx;
			}

			//расчитываем высоту строки
			float m_max_cy=0; // максимальная высота ячейки в строке
			for(j=0;j<cur_row->m_report_fields.GetSize();j++)
			{
				CaplReportField *field=cur_row->m_report_fields[j];
				field->m_y=fy+ty;

				if(0==field->m_cy || bCalcFieldHigh)
				{
					//расчитываем высоту ячейки по тексту
					if(field->m_text==_T(""))  field->m_cy=0;
					else
					{
						RECT rect_text;
						rect_text.left=0;
						rect_text.top=0;

						rect_text.right=(int)(10*(field->m_cx-3)); //3-отступы считаем в 0.1 мм
						rect_text.bottom=10000;


						if(field->m_font_index>=0)
							tmp_dc.SelectObject(fonts[field->m_font_index]);
						tmp_dc.DrawText(field->m_text,&rect_text,DT_WORDBREAK|DT_LEFT|DT_CALCRECT);

						float text_cy=(((float)(rect_text.bottom))/10) +1;// 1 - для отсупа
						field->m_cy=text_cy;
					}
					if(0!=field->m_image)
					{
						//расчитываем высоту ячейки по картинке
						CaplPdfPage *pdfPage=field->m_image->LoadImage();
						if(0!=pdfPage)
						{
							float cell_cx=field->m_cx-3; // Допустимая ширина с учетом отсупов
							float img_cx,img_cy;
							pdfPage->GetOriginalSizeIn_mm(img_cx,img_cy);

							if(img_cx<1 || img_cy<1)
							{
								buf.Format(APL_T("Не удалось определить размеры изображения '%s'"),LPCTSTR(field->m_image->m_name));
								AfxMessageBox(buf,MB_ICONSTOP|MB_OK);
							}
							else
							{
								float img_cx1=img_cx; // ширина которую будем использовть
								if(img_cx1>cell_cx) img_cx1=cell_cx; // Если ширина картинки больше ширины ячейки - берем ширину ячейки

								float img_cy1=img_cx1*img_cy/img_cx;

								/*if(img_cy1>0.75*fcy) // тупо закладываемся, что высота изображения не должна быть больше 3/4 высоты поля
								{
									img_cy1=(float)(0.75*fcy);
									img_cx1=img_cy1*img_cx/img_cy;
								}*/

								if(ty+img_cy1>fcy) // не влазит на страницу
								{
									if(ty<(0.3*fcy)) // Если заняли менее 30% - уменьшаем до оставшегося
									{
										img_cy1=fcy-(ty+1); //1мм на запас
										img_cx1=img_cy1*img_cx/img_cy;
									}
								}

								field->m_cy=img_cy1;
							}
							field->m_image->FreeImage();
						}
					}
				}
				if(field->m_cy>m_max_cy)m_max_cy=field->m_cy;
			}

			if(bDublicateRows)
			{
				// Тут делаем дубди ячеек и переносим осчавшуюся часть строк

				int nBaseFieldsCount=cur_row->m_report_fields.GetSize();
				CStringArray **pSa=0;

				int nNeewdRosws=0; // Сколько надо строк чтобы разместить всё

				// Разбиваем строки на массивы
				for(j=0;j<nBaseFieldsCount;j++)
				{
					CaplReportField *field=cur_row->m_report_fields[j];
					if(field->m_text==_T("")) continue;

					if(field->m_font_index>=0)
						tmp_dc.SelectObject(fonts[field->m_font_index]);

					CStringArray sa;
					if(DivideStringIntoParts(&tmp_dc, field->m_cx, field->m_text,sa))
					{
						// Строка была разделена на части
						// рисуем вниз по строчкам
						field->m_text=sa[0];

						if(0==pSa)
						{
							pSa=new CStringArray*[nBaseFieldsCount];
							int z;
							for(z=0;z<nBaseFieldsCount;z++) pSa[z]=0;
						}
						pSa[j]=new CStringArray;
						pSa[j]->Append(sa);

						if(sa.GetSize()>nNeewdRosws) nNeewdRosws=sa.GetSize();
					}
				}

				// Дорисовываем нужные ячейки
				if(nNeewdRosws>1 && pSa!=0)
				{
					for(j=0;j<nBaseFieldsCount;j++)
					{
						CaplReportField *field=cur_row->m_report_fields[j];
						CaplReportField *field_prev=field;
						CStringArray *sa=pSa[j];
						int k;
						for(k=1;k<nNeewdRosws;k++)
						{
							CaplReportField *field_1=new CaplReportField(field_prev);
							cur_row->m_report_fields.Add(field_1);
							field_1->m_text=_T("");
							if(0!=sa)
							{
								if(k<sa->GetSize()) field_1->m_text=sa->GetAt(k);
							}
 							field_1->m_y+=field->m_cy;
 							float h=(field_1->m_y+field->m_cy)-field->m_y;
 							if(h>m_max_cy) m_max_cy=h;
							field_prev=field_1;
						}
					}
				}

				if(0!=pSa)
				{
					for(j=0;j<nBaseFieldsCount;j++)	{ if(0!=pSa[j]) delete pSa[j];}
					delete pSa;
				}
			}
			if((ty+m_max_cy)>fcy) 
			{
				// На страницу не влезло.
				cur_row->m_report_fields.Clear();

				// обработка предыдущей строки, если ее нельзя переностить на следующую
				i--;
				while(i>=0)
				{
					CaplReportDataRow *prev_row=rows->GetAt(i);
					if(!prev_row->m_bKeepWithNext) break;
					//Предыдущую тоже надо перенести Вычищаем поля предыдущей строки со страницы
					int n,m;
					for(n=prev_row->m_report_fields.GetSize()-1;n>=0;n--)
					{
						for(m=curpage->m_fields.GetSize()-1;m>=0;m--)
						{
							if(curpage->m_fields.GetAt(m) == prev_row->m_report_fields.GetAt(n))
							{
								curpage->m_fields.Remove(m);
								break;
							}	}	}
					prev_row->m_report_fields.Clear();
					prev_row->m_report_fields.AutoKillReference=true; //возвращаем как должно быть
					i--;
				}
				curpage=0; // след. цикл с новой страницы

				continue;
			}
			else
			{
				ty+=m_max_cy;
				cur_row->m_report_fields.AutoKillReference=false; // т.к. переносим в страницу
				for(j=0;j<cur_row->m_report_fields.GetSize();j++)
				{
					CaplReportField *field=cur_row->m_report_fields[j];
					curpage->m_fields.Add(field);
				}

				if(true==cur_row->m_bCopyToNextPage)
					row4CopyToNextPage=cur_row;
			}
		}
		if(0!=fonts) delete [] fonts;
	}

	// Удаляем неиспользованные страницы
	if (i_page<0) i_page=0; // чтобы осталась хоть одна страница (для бланков без таблиц)
	for(i=m_pages.GetSize()-1; i>i_page; i--) 
		m_pages.Remove(i);


	// Проставляем номера старниц
	for(i=0;i<m_pages.GetSize();i++)
	{
		CaplReportPage *page=m_pages[i];
		for(j=0;j<page->m_fields.GetSize();j++)
		{
			CaplReportField *field=page->m_fields[j];
			if(field->m_field==_T("")) continue;
			if(field->m_field==_T("#page")) 
			{
				if(m_pages.GetSize()==1) field->m_text=_T(""); // Если страница одна, то номер не пишется
				else {	field->m_field=_T(""); field->m_text.Format(_T("%i"),i+1);}
			}
			else if(field->m_field==_T("#pages")) {field->m_field=_T(""); field->m_text.Format(_T("%i"),m_pages.GetSize());}
		}
	}

	SaveReport(report_file);
	//AfxMessageBox(APL_T("Готово!"));
	return true;
}

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

// Делим строку на части так, чтобы части влезли в cx  
// Возвращает true, если разделялось, и false, если разделять не надо

bool CaplReport::DivideStringIntoParts(CDC *pDC, float cx, CString &text, CStringArray &sa )
{
	sa.RemoveAll();

	RECT rect;
	rect.left=0;
	rect.top=0;
	rect.right=100000;
	rect.bottom=100000;

	pDC->DrawText(text,&rect,DT_SINGLELINE|DT_LEFT|DT_CALCRECT);

	float text_cx=(((float)(rect.right))/10) +1;// 1 - для отсупа
	if(text_cx<=cx) return false;

	int i=0,j,j_ok;

	while(true) // Каждый цикл выдирает новую строчку
	{
		// Пропускаем разделители вначале
		while(true)
		{
			TCHAR c= text[i];
			if(c==_T('\0')) return (sa.GetSize()>1);
			if( c==_T(' ') ||  c==_T('\r') || c==_T('\n') || c==_T('\t')) {i++; continue;}
			break;
		}
		j=i+1;
		j_ok=j;


		bool bEndText=false;
		CString buf;

		while (true) // каждый цикл добавляет "слово"
		{
			// Ищем след. разделитель
			while (true)
			{
				TCHAR c= text[j]; 
				if(c==_T('\0')) {bEndText=true;  break;}
				if(c==_T('\r') || c==_T('\n') || c==_T('\t')) break;
				
				if( c==_T(' '))
				{
					// Тут  проверяeм КЮФУ, ГОСТ, ОСТ, ТУ
					// Если перед пробелом все русские заглавные, то переносить нельзя
					int k=1;
					buf=_T("");
					bool bIsObozn=true;
					while((j-k)>i)
					{
						TCHAR ck= text[j-k];
						if(ck==_T(' ')) break;
						if( !( (ck>=_T('А') && (ck<=_T('Я'))) )) {bIsObozn=false; break;} // Смотрим только русские заглавнык
						
						buf+=ck;
						k++;
					}
					if(!bIsObozn) break;
				}

				if(c==_T('-')) // Это может быть чать обозначения
				{
					if(j>0)
					{
						TCHAR ck= text[j-1];
						if(! ( (ck>=_T('0') && ck<=_T('9')) || (ck>=_T('А') && ck<=_T('Я')) || (ck>=_T('A') && ck<=_T('Z')) ))
							break;
					}
				}
				j++;
			}

			CString part_str=text.Mid(i,j-i);

			rect.right=100000;
			rect.bottom=100000;
			pDC->DrawText(part_str,&rect,DT_SINGLELINE|DT_LEFT|DT_CALCRECT);

			float text_cx=(((float)(rect.right))/10) +1;// 1 - для отсупа


			if(text_cx<=cx) 
			{	
				// Слово влезло
				j_ok=j; j++;
				if(bEndText)  {sa.Add(part_str); return (sa.GetSize()>1); }
			} 
			else
			{
				if(j_ok==(i+1)) // не влезло первое же слово
				{
					// не влезло первое же слово
					sa.Add(part_str);  // все равно добавляем 1 слово, сколько влезет, столько и отрисуется
					i=j;
				}
				else
				{
					part_str=text.Mid(i,j_ok-i);
					sa.Add(part_str);
					i=j_ok;
				}
				break;
			}
		}
	}
	return (sa.GetSize()>1);
}

//********************************************************
bool CaplReport::SaveToTiff(LPCTSTR tiff_file, int dpi, BOOL bColor, DWORD CompressionMode)
{
	//AfxMessageBox(fiff_file);
	if(0==tiff_file) return false;
	if(_T('0')==tiff_file[0]) return false;

	::SetFileAttributes(tiff_file, FILE_ATTRIBUTE_NORMAL);
	::DeleteFile(tiff_file);

	if(m_pages.GetSize()<1) return false;
	if(dpi<50) return false;

	CDC *pDC=AfxGetMainWnd()->GetDC();

	bool bSinfleFile=(1==m_pages.GetSize());
	bool bLastResult=false;

	CStringArray saFiles;
	CString buf,sMask=tiff_file,sTmpFile=tiff_file;
	
	int i;

	i=sTmpFile.ReverseFind(_T('.'));
	if(i>0) sMask.Insert(i,_T("_%i"));
	else sMask+=_T("-%i");

	long wait_dlg=aplStartWaitDlg(APL_T("Подготовка изображения"),APL_T("Преобразование в TIFF"));

	for(i=0;i<m_pages.GetSize();i++)
	{
		bLastResult=false;
		CaplReportPage *page=new CaplReportPage(m_pages[i]);
		if(0==page) continue;

		buf.Format(APL_T("Преобразование страницы %i / %i"),i+1,m_pages.GetSize());
		aplSetTextWaitDlg(wait_dlg,buf);

		if(!bSinfleFile) sTmpFile.Format(sMask,i);

		page->m_num=i;
		float orig_cx, orig_cy;
		page->GetOriginalSizeIn_mm(orig_cx,orig_cy);

		int cx1=(int) (orig_cx*dpi/25.4);
		if(page->Make2Cx(pDC,cx1,0,0))
		{
			CBitmap *bmp=page->GetCurBitmap();
			bLastResult=CaplImage::SaveBitmapAs(*bmp, sTmpFile, dpi, bColor, CompressionMode);
			if(bLastResult) saFiles.Add(sTmpFile);
		}
		delete page;
	}
	AfxGetMainWnd()->ReleaseDC(pDC);

	aplSetTextWaitDlg(wait_dlg,APL_T("Объединение страниц"));

	if(!bSinfleFile) bLastResult=CaplImage::UnificationTIFF(&saFiles,tiff_file);

	aplEndWaitDlg(wait_dlg);

	return bLastResult;
}