#include "afxmt.h"
#include "RawLine.h"
#include "PreprocFuncs.h"
#include "RectElementMapping.h"
#include "TextLocate.h"

CCriticalSection g_csChunks;

bool SaveaBMP(const unsigned char *rgb, const char* FileName,
			  int xWidth, int yHeight, int BPP, bool bFlip, int nWidthStep);
//////////////////////////////CParagraph///////////////////////////////////////
void CParagraph::AddRawLine(CRawLine *pRl){
	if(!pRl) return;
	m_nTotalHeight += pRl->GetHeight() * pRl->size();
	m_nTotalCount += pRl->size();
	(*this) |= *pRl;
	pRl->m_nParagraphIndx = m_nId;
	pRl->m_nLineIndx = size();
	push_back(pRl);
	m_vLines2Chunks.push_back(NULL);
}

CParagraph::CParagraph(int nId, bool bBoW, BOOL bIsEmbedded) : m_nId(nId), m_nTotalHeight(0), m_nTotalCount(0), 
m_bIsEmbedded(bIsEmbedded), 
m_bBoW(bBoW), 
m_linesTouchingFrame(0, 0, 0, 0), 
m_bIsMainParagraph(false),
m_bIsHeader(false),
m_nArea(0),
m_nLinesOrientation(-1),
m_nTotalWords(0),
m_nTotalComponents(0),
m_bDoNotFilter(FALSE)
{
	SetRectEmpty();
}

void CParagraph::SetDoNotFilter(BOOL bDoNotFilter) {
	m_bDoNotFilter = bDoNotFilter;
}

BOOL CParagraph::GetDoNotFilter() const {
	return m_bDoNotFilter;
}


int CParagraph::GetReferencePoints(CRect roi, vector<CPoint> & vXY, vector<int> & vY)const{
	int nSalientLines = 0;
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i && (*i)->m_bIsSalient){
			(*i)->GetReferencePoints(roi, vXY, vY);
			++nSalientLines;
		}
	}
	return nSalientLines;
}


int CParagraph::GetReferencePoints(int nFirstLine, int nLastLine,
								   CPoint & origin, CRect & bb,
								   vector<CPoint> & vXY, vector<int> & vY)const{
	int i;
	bb.SetRectEmpty();
	for(i = nFirstLine; i <= nLastLine; ++i){
		if(at(i)){
			bb |= *at(i);
		}
	}
	origin = bb.CenterPoint();
	int nSalientLines = 0;
	int nTopPos = bb.top;
	int nPrev = -1;
	for(i = nFirstLine; i <= nLastLine; ++i){
		if(at(i) && at(i)->m_bIsSalient){
			if(nPrev >= 0) {
				int nDist = at(nPrev)->GetAverageDistance(at(i));
				if(nDist <= 0) continue;
				nTopPos += nDist;
			}
			nPrev = i;
			at(i)->GetReferencePoints(origin, vXY, vY, &nTopPos);
			++nSalientLines;
		}
	}
	return nSalientLines;
}

int CParagraph::GetCoeficients(CRect roi, int & nOrderX, int & nOrderY, vector<float> & vCoefs)const{
	vector<float> vPowers;
	vector<CPoint> vXY;
	vector<int> vY;
	int nSalientLineCount = GetReferencePoints(roi, vXY, vY);
	int nPoints = vY.size();
	if(nSalientLineCount < 1 || nPoints < 20){
		return 0;
	}
	int nH = GetHeight();
	if(nH * 15 >= Width()){
		nOrderX = 1;
	}
	else if(nH * 30 >= Width()){
		nOrderX = 2;
	}
	else{
		nOrderX = 3;
	}
	if(nSalientLineCount < 3){
		nOrderY = 1;
	}
	else if(nSalientLineCount < 7){
		nOrderY = 2;
	}
	else{
		nOrderY = 3;
	}
	if(nOrderY > nOrderX) nOrderY = nOrderX;
	//nOrderX = 1;
	//nOrderY = 1;
	UINT nCoefs = (nOrderY + 1) * (nOrderY + 2) / 2 + (nOrderX - nOrderY) * (nOrderY + 1);
	float **ppM = fmatrix(vXY.size(), nCoefs), **ppY = fmatrix(vY.size(), 1);
	UINT i;
	for(i = 1; i <= vXY.size(); ++i){
		ppY[i][1] = float(vY[i - 1]);
		float x = float(vXY[i-1].x), y = float(vXY[i-1].y);
		GetXYPowers(x, y, nOrderX, nOrderY, vPowers);
		for(int j = 0; j < int(nCoefs); ++j){
			ppM[i][j + 1] = vPowers[j];
		}
	}
	float **ppMT = transpose(ppM, vXY.size(), nCoefs);
	float **ppTMT = mat_mat_mult(ppMT, nCoefs, vXY.size(), ppM, nCoefs);
	float **ppTMY = mat_mat_mult(ppMT, nCoefs, vXY.size(), ppY, 1);
	if(gaussj(ppTMT, nCoefs, ppTMY, 1)){
		for(i = 1; i <= nCoefs; ++i){
			vCoefs.push_back(ppTMY[i][1]);
		}
	}
	else{
		nCoefs = 0;
	}
	free_fmatrix(ppTMY, nCoefs);
	free_fmatrix(ppTMT, nCoefs);
	free_fmatrix(ppMT, nCoefs);
	free_fmatrix(ppY, vY.size());
	free_fmatrix(ppM, vXY.size());
	return nCoefs;
}

int CParagraph::GetCoeficients(int nFirstLine, int nLastLine, CPoint & origin, CRect & bb,
							   int & nOrderX, int & nOrderY, vector<float> & vCoefs)const{
	vector<float> vPowers;
	vector<CPoint> vXY;
	vector<int> vY;
	int nSalientLineCount = GetReferencePoints( nFirstLine, nLastLine, origin, bb, vXY, vY);
	int nPoints = vY.size();
	if(nSalientLineCount < 1 || nPoints < 20){
		return 0;
	}
	int nH = GetHeight();
	if(nH * 15 >= Width()){
		nOrderX = 1;
	}
	else if(nH * 30 >= Width()){
		nOrderX = 2;
	}
	else{
		nOrderX = 3;
	}
	if(nSalientLineCount < 3){
		nOrderY = 1;
	}
	else if(nSalientLineCount < 7){
		nOrderY = 2;
	}
	else{
		nOrderY = 3;
	}
	if(nOrderY > nOrderX) nOrderY = nOrderX;
	//nOrderX = 1;
	//nOrderY = 1;
	UINT nCoefs = (nOrderY + 1) * (nOrderY + 2) / 2 + (nOrderX - nOrderY) * (nOrderY + 1);
	float **ppM = fmatrix(vXY.size(), nCoefs), **ppY = fmatrix(vY.size(), 1);
	UINT i;
	for(i = 1; i <= vXY.size(); ++i){
		ppY[i][1] = float(vY[i - 1]);
		float x = float(vXY[i-1].x), y = float(vXY[i-1].y);
		GetXYPowers(x, y, nOrderX, nOrderY, vPowers);
		for(int j = 0; j < int(nCoefs); ++j){
			ppM[i][j + 1] = vPowers[j];
		}
	}
	float **ppMT = transpose(ppM, vXY.size(), nCoefs);
	float **ppTMT = mat_mat_mult(ppMT, nCoefs, vXY.size(), ppM, nCoefs);
	float **ppTMY = mat_mat_mult(ppMT, nCoefs, vXY.size(), ppY, 1);
	if(gaussj(ppTMT, nCoefs, ppTMY, 1)){
		for(i = 1; i <= nCoefs; ++i){
			vCoefs.push_back(ppTMY[i][1]);
		}
	}
	else{
		nCoefs = 0;
	}
	free_fmatrix(ppTMY, nCoefs);
	free_fmatrix(ppTMT, nCoefs);
	free_fmatrix(ppMT, nCoefs);
	free_fmatrix(ppY, vY.size());
	free_fmatrix(ppM, vXY.size());
	return nCoefs;
}

int CParagraph::GetOptimalBinarizationThreshold(int nFirstLine, int nLastLine) const {
	int nAvgGrey = 0, nAvgBackGrnd = 0, nCount = 0;
	for(int i = nFirstLine; i <= nLastLine; ++i){
		if(at(i)){
			int nSize = int(at(i)->size());
			nAvgGrey += at(i)->m_nAvgGrey * nSize;
			nAvgBackGrnd += at(i)->m_nAvgBackGrnd * nSize;
			nCount += nSize;
		}
	}
	if(nCount <= 0) {
		return -1;
	}
	
	nAvgGrey /= nCount;
	nAvgBackGrnd /= nCount;
	if(!m_bBoW) {
		nAvgGrey = 255 - nAvgGrey;
		nAvgBackGrnd = 255 - nAvgBackGrnd;
	}
	int nContrast = nAvgBackGrnd - nAvgGrey;
	int nThreshold = int(0.9 * 256);
	if(nAvgGrey < 80 && nContrast >= 60) {
		 nThreshold = int(0.85 * 256);
	}
	else if(nAvgGrey  >= 120) {
		 nThreshold = int(0.95 * 256);
	}
	else if(nContrast < 20 ||  nAvgBackGrnd < 80) {
		 nThreshold = int(0.98 * 256);
	}
	return nThreshold;
}

//////////////////////////////////////////////////////////////////////////////
int GetValue(int nX, int nY, int nXCenter, int nYCenter, int nOrderX, int nOrderY, const vector<float> & vCoefs){
	vector<float> vPowers;  
	GetXYPowers(float(nX - nXCenter), float(nY - nYCenter), nOrderX, nOrderY, vPowers);
	float fSum = 0.0f;
	for(vector<float>::const_iterator i = vPowers.begin(), j = vCoefs.begin(); i != vPowers.end(); ++i, ++j){
		fSum += *i * *j;
	}
	return int(fSum) + nYCenter;
}

int GetCorrectedY(int nX, int nOriginalY, int nXCenter, int nYCenter, const LONGLONG *pCoefs, 
				  LONGLONG (*GetValueFunction)(LONGLONG, LONGLONG, const LONGLONG *),
				  int nStep = 10, int nMaxIter = 1000){
					  int nIter = 0;
					  int nY = nOriginalY, nUp, nLow;
					  int nVal = int(GetValueFunction(nX - nXCenter, nY - nYCenter, pCoefs) >> CURVATURE_RES_SHIFT) + nYCenter;
					  if(nVal > nOriginalY){
						  nUp = nY;
						  for(nY -= nStep; (nVal = int(GetValueFunction(nX - nXCenter, nY - nYCenter, pCoefs) >> CURVATURE_RES_SHIFT) + nYCenter) > 
							  nOriginalY && nIter < nMaxIter; nY -= nStep, ++nIter);
							  if(nVal < nOriginalY){
								  nLow = nY;
							  }
							  else if(nVal > nOriginalY){
								  return nOriginalY;
							  }
							  else{
								  return nY;
							  }
					  }
					  else if(nVal < nOriginalY){
						  nLow = nY;
						  for(nY += nStep; (nVal =  int(GetValueFunction(nX - nXCenter, nY - nYCenter, pCoefs) >> CURVATURE_RES_SHIFT) + nYCenter) < 
							  nOriginalY  && nIter < nMaxIter; nY += nStep, ++nIter);
							  if(nVal > nOriginalY){
								  nUp = nY;
							  }
							  else if(nVal < nOriginalY){
								  return nOriginalY;
							  }
							  else{
								  return nY;
							  }
					  }
					  else{
						  return nY;
					  }
					  while(nUp > nLow && nIter < nMaxIter){
						  nY = (nUp + nLow) / 2;
						  nVal =  int(GetValueFunction(nX - nXCenter, nY - nYCenter, pCoefs) >> CURVATURE_RES_SHIFT) + nYCenter;
						  if(nVal > nOriginalY){
							  nUp = nY;
							  if(nUp - nLow <= 1){
								  return nY;
							  }
						  }
						  else if(nVal < nOriginalY){
							  nLow = nY;
							  if(nUp - nLow <= 1){
								  return nY;
							  }
						  }
						  else{
							  return nY;
						  }
						  ++nIter;
					  }
					  if(nIter < nMaxIter){
						  return nY;
					  }
					  return nOriginalY;
}

CRect CParagraph::GetCorrectedBoundBox(const CPoint & origin, CRect bb, int nFirstLine, int nLastLine,
									   int nOrderX, int nOrderY, const LONGLONG *pCoefs)const{
	CPoint cntr(origin);
	if(size() < 1){
		return bb;
	}
	GET_VALUE_FUNCTION getValue = GetYCorrectionFunction(nOrderX, nOrderY);
	if(!getValue) return bb;
	CRawLine *pLtop = at(nFirstLine);
	CRawLine *pLbottom = at(nLastLine);
	if(!pLtop || !pLbottom || pLtop->size() < 1 || pLbottom->size() < 1){
		return bb;
	}
	CRawLine::const_iterator i;
	int nTop = GetCorrectedY(pLtop->begin()->left, pLtop->begin()->top, origin.x, origin.y, pCoefs, getValue);
	int nBottom = GetCorrectedY(pLbottom->begin()->left, pLbottom->begin()->bottom, origin.x, origin.y, pCoefs, getValue);
	for(;;) {
		for(i = pLtop->begin(); i != pLtop->end(); ++i){
			CRect b(*i);
			int nT0 = GetCorrectedY(b.left, b.top, origin.x, origin.y, pCoefs, getValue);
			int nT1 = GetCorrectedY(b.right, b.top, origin.x, origin.y, pCoefs, getValue);
			if(nTop > nT0) nTop = nT0;
			if(nTop > nT1) nTop = nT1;
		}
		if(!pLtop->m_bIsSalient && nFirstLine < nLastLine) {
			pLtop = at(++nFirstLine);
		}
		else {
			break;
		}
	}

	for(;;) {
		for(i = pLbottom->begin(); i != pLbottom->end(); ++i){
			CRect b(*i);
			int nB0 = GetCorrectedY(b.left, b.bottom, origin.x, origin.y, pCoefs, getValue);
			int nB1 = GetCorrectedY(b.right, b.bottom, origin.x, origin.y, pCoefs, getValue);
			if(nBottom < nB0) nBottom = nB0;
			if(nBottom < nB1) nBottom = nB1;
		}
		if(!pLbottom->m_bIsSalient && nFirstLine < nLastLine) {
			pLbottom = at(--nLastLine);
		}
		else {
			break;
		}
	}

	bb.top = nTop; 
	bb.bottom = nBottom;
	return bb;
}

bool CParagraph::IsRunningIntoFrame()const{
	int nMaxCount = 0;
	if(m_nLinesOrientation == HORIZONTAL){
		nMaxCount = max(m_linesTouchingFrame.left, m_linesTouchingFrame.right);
	}
	else{
		nMaxCount = max(m_linesTouchingFrame.top, m_linesTouchingFrame.bottom);
	}
	return nMaxCount * 3 >= (int)size();
}

bool CParagraph::IsTouchingFrame(int nMaxLinesToIgnore)const{
	int nMaxCount = m_linesTouchingFrame.left;
	if(m_linesTouchingFrame.right > nMaxCount) nMaxCount = m_linesTouchingFrame.right;
	if(m_linesTouchingFrame.top > nMaxCount) nMaxCount = m_linesTouchingFrame.top;
	if(m_linesTouchingFrame.bottom > nMaxCount) nMaxCount = m_linesTouchingFrame.bottom;
	return nMaxCount * 3 > (int)size() || (nMaxLinesToIgnore >= 0 && nMaxCount > nMaxLinesToIgnore);
}

int CParagraph::GetNumTotalWords(){
	if(empty()) {
		return 0;
	}
	if(m_nTotalWords > 0) {
		return m_nTotalWords;
	}
	m_nTotalWords = 0;
	for(const_iterator i = begin(); i != end(); ++i) {
		if(*i) {
			m_nTotalWords += (*i)->CountWords();
		}
	}
	return m_nTotalWords;
}

int CParagraph::GetTotalNumOfComponents(){
	if(empty()) {
		return 0;
	}
	if(m_nTotalComponents > 0) {
		return m_nTotalComponents;
	}
	m_nTotalComponents = 0;
	for(const_iterator i = begin(); i != end(); ++i) {
		if(*i) {
			m_nTotalComponents += (*i)->size();
		}
	}
	return m_nTotalComponents;
}

int CParagraph::GetMaxNumOfComponentsInLine() const {
	if(empty()) {
		return 0;
	}
	int nComponents = 0;
	for(const_iterator i = begin(); i != end(); ++i) {
		if(*i && (*i)->size() > nComponents) {
			nComponents = (*i)->size();
		}
	}
	return nComponents;
}


COcrChunk *CParagraph::AddOcrChunk(int nFirstLine, int nLastLine, IplImage *pGreyImg, UCHAR *p1BppImg,
int nOrderX, int nOrderY, const LONGLONG *pCoefs, const CRect & bb, int nScale, const CPoint & origin) {
	CSingleLock sl(&g_csChunks);
	sl.Lock();
	if(sl.IsLocked()) {
		COcrChunk *pChunk = new COcrChunk(int(m_mapOcrChunks.size()), this, nFirstLine, nLastLine, pGreyImg, p1BppImg,
			nOrderX, nOrderY, pCoefs, &bb, nScale, &origin);
		m_mapOcrChunks.insert(pair<int, COcrChunk*>(m_mapOcrChunks.size(), pChunk));
		for(int i = nFirstLine; i < nLastLine; ++i) {
			m_vLines2Chunks[i] =  pChunk;
		}
		return pChunk;
	}
	return NULL;
}

#define NOT_PRONAUNCED_CHARACTERS L"\r\n\t .,!?\\/|)([]}{li~\'><-_"
bool CParagraph::NoText() const {
	Dictionary *pDict = Dictionary::Instance();
	for(map<int, COcrChunk*>::const_iterator i = m_mapOcrChunks.begin(); i != m_mapOcrChunks.end(); ++i) {
		if(i->second && (!i->second->m_upSideUpTp.IsRectEmpty() || !i->second->m_upSideDownTp.IsRectEmpty())) { 
			wstring sText;
			i->second->m_upSideUpTp.GetFilteredText(sText, 0, 0, pDict);
			if(sText.find_first_not_of(NOT_PRONAUNCED_CHARACTERS) == string::npos){
				i->second->m_upSideDownTp.GetFilteredText(sText, 0, 0, pDict);
				if(sText.find_first_not_of(NOT_PRONAUNCED_CHARACTERS) != string::npos){
					return false;
				}
			}
			else{
				return false;
			}
		}
	}
	return true;
}
	
bool CParagraph::OCredAlready(int nLineIndx) const {
	bool bOcred = false;
	CSingleLock sl(&g_csChunks);
	sl.Lock();
	if(sl.IsLocked()) {
		COcrChunk *pChunk = m_vLines2Chunks[nLineIndx];
		if(pChunk) {
			bOcred = pChunk->GetOcred();
		}
		return bOcred;
	}
	return true;
}

BOOL CParagraph::CheckIfHeader() const {
	if(size() > 3 || Width() < GetHeight() * 3) {
		return FALSE;
	}
	set<CParagraph*>::const_iterator i;
	for(i = m_upNeighbors.begin(); i != m_upNeighbors.end(); ++i) {
		if(*i && *i != this) {
			if((*i)->size() >= 5 && (*i)->GetHeight() < GetHeight() && XOverlap(**i, *this) * 3 >=  Width()
				&& YOverlap(**i, *this) > -GetHeight() * 3) {
				return TRUE;
			}
		}
	}
	for(i = m_downNeighbors.begin(); i != m_downNeighbors.end(); ++i) {
		if(*i && *i != this) {
			if((*i)->size() >= 5 && (*i)->GetHeight() < GetHeight() && XOverlap(**i, *this) * 3 >=  Width()
				&& YOverlap(**i, *this) > -GetHeight() * 3) {
				return TRUE;
			}
		}
	}
	return FALSE;
}

////////////////////////////////////CRawLines//////////////////////////////////
CRawLines::~CRawLines(){
	clear();
}

void CRawLines::clear(){
	for(iterator i = begin(); i != end(); ++i){
		if(*i) delete *i;
	}
	vector<CRawLine*>::clear();
	for(vector<CParagraph*>::iterator ip = m_vParagraphs.begin(); ip != m_vParagraphs.end(); ++ip){
		if(*ip) delete *ip;
	}
	m_vParagraphs.clear();
	m_vRegularOrder.clear();
	m_vForceSingleOrder.clear();
	m_bUpSideDown = FALSE;
	m_bSparseText = FALSE;
	m_bColumnLike = FALSE;
	m_nColumns = 0;
	m_nHeaders = 0;
}

void CleanMargins(UCHAR *p1bppImg, int nWidth, int nHeight, int nMargin){
	nWidth >>= 3;
	memset(p1bppImg, 255, nWidth * nMargin);
	p1bppImg += nWidth * nMargin;
	int nM = nMargin >> 3;
	int nW = nWidth  - nM;
	for(UCHAR *pE = p1bppImg + nWidth * (nHeight - 2 * nMargin); p1bppImg < pE; p1bppImg += nWidth){
		memset(p1bppImg, 255, nM); 
		memset(p1bppImg + nW, 255, nM); 
	}
	memset(p1bppImg, 255, nWidth * nMargin);
}

UCHAR* AddMargins21bppImg(const UCHAR *p8bppImg, int nWidth, int nHeight, int nMargin, int &nNewWidth, int &nNewHeight){
	nNewWidth = nWidth + nMargin * 2;
	nNewHeight = nWidth + nMargin * 2;
	int nNewWidthStep = nNewWidth / 8;
	int nWidthStep = nWidth / 8;
	UCHAR *pNewBuf = new UCHAR[nNewWidthStep * nNewHeight];
	memset(pNewBuf, 255, nNewWidthStep * nNewHeight);
	UCHAR *pN = pNewBuf + nMargin * nNewWidthStep + nMargin/8;
	for(const UCHAR *pE = p8bppImg + nWidth * nHeight; p8bppImg < pE; p8bppImg += nWidthStep, pN+=nNewWidthStep){
		memcpy(pN, p8bppImg, nWidthStep);
	}
	return pNewBuf; 
}

void MarkLinesTouchingFrame(CParagraph *pP, const ImgRotationInfo &iri, int nMinDistanceHoriz, int nMinDistanceVert){
	int minDistWidth, minDistHeight;
	if(iri.GetLinesOrientation() == HORIZONTAL){
		minDistWidth = nMinDistanceHoriz;
		minDistHeight = nMinDistanceVert;
	}
	else{
		minDistWidth = nMinDistanceVert;
		minDistHeight = nMinDistanceHoriz;
	}
		
	CRect count(0, 0, 0, 0);
	for(CParagraph::iterator i = pP->begin(); i != pP->end(); ++i){
		if(*i){
			CRect dists = iri.GetMinRoiDistancesFromImageFrame(**i);
			if(dists.left <=  minDistWidth){
				(*i)->m_nTouchingFrame |= TOUCHES_LEFT;
				++count.left;
			}
			if(dists.right <=  minDistWidth){
				(*i)->m_nTouchingFrame |= TOUCHES_RIGHT;
				++count.right;
			}
			if(dists.top <=  minDistHeight){
				(*i)->m_nTouchingFrame &= TOUCHES_TOP;
				++count.top;
			}
			if(dists.bottom <=  minDistHeight){
				(*i)->m_nTouchingFrame &= TOUCHES_BOTTOM;
				++count.bottom;
			}
		}
	}
	pP->SetLinesTouchingFrame(count);
	pP->SetLinesOrientation(iri.GetLinesOrientation());
}

void TranslateCoeficientsToLongLong(const vector<float> & vCoefs, int nOrderX, int nOrderY, int nCoefs, LONGLONG *pCoefs){
	int i, nIndx = 0;
	pCoefs[nIndx] = LONGLONG(vCoefs[nIndx] * (1 << CURVATURE_RES_SHIFT));
	++nIndx;
	for(i = 0; i <= min(1, nOrderY) && nIndx < nCoefs; ++i){
		pCoefs[nIndx] = LONGLONG(vCoefs[nIndx] * (1 << CURVATURE_COEFS_SHIFT_1));
		++nIndx;
	}
	for(i = 0; i <=  min(2, nOrderY)&& nIndx < nCoefs; ++i){
		pCoefs[nIndx] = LONGLONG(vCoefs[nIndx] * (1 << CURVATURE_COEFS_SHIFT_2));
		++nIndx;
	}
	for(i = 0; i <= min(3, nOrderY) && nIndx < nCoefs; ++i){
		pCoefs[nIndx] = LONGLONG(vCoefs[nIndx] * (1 << CURVATURE_COEFS_SHIFT_3));
		++nIndx;
	}
}

int CRawLines::MarkLinesTouchingFrame(const ImgRotationInfo &iri, int nMinDistanceHoriz, int nMinDistanceVert){
	int nArea = 0, nMaxArea = 0, nMainIndx = -1;
	m_nTotalParagraphsArea = 0;
	for(UINT i = 0; i != m_vParagraphs.size(); ++i){
		CParagraph *pP = m_vParagraphs[i];
		if(pP){
			nArea = pP->Width() * pP->Height();
			m_nTotalParagraphsArea += nArea;
			pP->SetArea(nArea);
			if(nArea > nMaxArea){
				nMaxArea = nArea;
				nMainIndx = i;
			}
			::MarkLinesTouchingFrame(pP, iri, nMinDistanceHoriz, nMinDistanceVert);
		}
	}
	if(nMainIndx >= 0){
		m_vParagraphs[nMainIndx]->SetMainParagraph(true);
	}
	return m_nMainParagraphIndx = nMainIndx;
}

void AddParagraphNeighbors(multimap <int, Vertice>::const_iterator itTop,
							multimap <int, Vertice>::const_iterator itBottom, const multimap <int, Vertice> & mapX2indx, 
							void *pParams) {
	vector<CParagraph*> *pParagraphs = (vector<CParagraph*> *)pParams;
	CParagraph *pP = pParagraphs->at(itTop->second.m_nId);
	if(pP) {
		for(multimap <int, Vertice>::const_reverse_iterator ri(itTop); ri != mapX2indx.rend(); ++ri) {
			CParagraph *pPn = pParagraphs->at(ri->second.m_nId);
			if(pPn) {
				pP->AddUpNeighbor(pPn);
			}
		}
		for(multimap <int, Vertice>::const_iterator i(itBottom); i != mapX2indx.end(); ++i) {
			CParagraph *pPn = pParagraphs->at(i->second.m_nId);
			if(pPn) {
				pP->AddDownNeighbor(pPn);
			}
		}
	}
}

void CRawLines::FindParagraphNeighbors() {
	IterateThroughColumns(m_vParagraphs, AddParagraphNeighbors, NULL, &m_vParagraphs);
}

BOOL CRawLines::AnalyzePage(const ImgRotationInfo &iri, int nMinDistanceHoriz, int nMinDistanceVert){
	MarkLinesTouchingFrame(iri, nMinDistanceHoriz, nMinDistanceVert);
	CParagraph *pMainParagraph = m_vParagraphs[m_nMainParagraphIndx];
	if(pMainParagraph->GetMaxNumOfComponentsInLine() < 7) {
		return FALSE;
	}
	if(pMainParagraph->size() < 5) {
		m_bSparseText = TRUE;
		return TRUE;
	}
	if(pMainParagraph->Width() < pMainParagraph->GetHeight() * 60) {
		m_bColumnLike = TRUE;
	}
	FindParagraphNeighbors();
	m_nColumns = 1;
	m_nHeaders = 1;
	CRect pageRange(*pMainParagraph);
	int nNonTouchingMaxIndx = -1, nNonTouchMaxArea = 0;
	for(int i = 0; i < m_vParagraphs.size(); ++i) {
		CParagraph *pP = m_vParagraphs[i];
		if(pP->GetNumTotalWords() > 40) {
			pP->SetDoNotFilter();
		}
		if(pP  && !pP->IsRunningIntoFrame()) {
			if(nNonTouchingMaxIndx < 0 || pP->GetArea() > nNonTouchMaxArea) {
				nNonTouchingMaxIndx = i;
				nNonTouchMaxArea = pP->GetArea();
			}
			if(pP->CheckIfHeader()) {
				pP->SetHeader();
				++m_nHeaders;
			}
			if(i != m_nMainParagraphIndx) {
				CParagraph *pP = m_vParagraphs[i];
				if(pP->GetArea() * 5 >=  pMainParagraph->GetArea()
					&& XOverlap(CRect(*pP), pageRange) <= 0) {
						pageRange.UnionRect(pageRange, *pP);
						++m_nColumns;
				}
			}
		}
		if(nNonTouchingMaxIndx > 0 && 
			m_vParagraphs[i]->size() >= 3
			&& m_vParagraphs[nNonTouchingMaxIndx]->Width() * 3 
			>=  pMainParagraph->Width() * 2) {
				pMainParagraph->SetMainParagraph(false);
				m_vParagraphs[nNonTouchingMaxIndx]->SetMainParagraph(true);
				m_nMainParagraphIndx = nNonTouchingMaxIndx;
		}
	}
	return TRUE;
}

UCHAR* CRawLines::GetParagraphNBitmap(int nParagraph, int nFirstLine, int nLastLine, const UCHAR *p8bppImg, const ImgRotationInfo &iri, 
									  int nMinBitmapHeight, int nMargin, int nThreshold,
									  int &nBitmapWidth, int &nBitmapHeight, bool bFixCurvature, int nFixCurvatureOptimalLinesCount,
									  UCHAR *pDest, IplImage **ppGreyImg, int nMaxDestBufSize, int & nStatus,
									  int *pOrderX, int *pOrderY, LONGLONG *pCoefs, int *pScale, CRect *pBB, CPoint *pOrigin)const {
  nStatus = 0;
  int nScale = 1 << SCALE_FXPNT;
  CParagraph *pP = m_vParagraphs[nParagraph];
  BOOL bWoB = !pP->GetBoW();
  if(!pP || pP->size() < 1 || pP->GetHeight() <= 5 || pP->Width() <= 5)
	  return NULL;

  if(nFirstLine >= (int)pP->size()){
	  return NULL;
  }
  if(nLastLine >= (int)pP->size()){
	  nLastLine = pP->size() - 1;
  }
  int nCoefs = 0;
  vector<float> vCoefs;
  int nOrderX = 0, nOrderY = 0;

//  nThreshold = pP->GetOptimalBinarizationThreshold(nFirstLine, nLastLine);

  CPoint origin(pP->CenterPoint());
  CRect bb(*pP);
  if(bFixCurvature){
	  int nExtraLines = nFixCurvatureOptimalLinesCount - nLastLine + nFirstLine - 1;
	  int nFcl = max(nFirstLine - nExtraLines / 2, 0);
	  int nLcl = min(nFcl + nFixCurvatureOptimalLinesCount - 1, (int)pP->size() - 1);
	  nCoefs = pP->GetCoeficients(nFcl, nLcl, origin, bb, nOrderX, nOrderY, vCoefs);
	  if(nCoefs > 0){
		  TranslateCoeficientsToLongLong(vCoefs, nOrderX, nOrderY, nCoefs, pCoefs);
		  CRect newBb = pP->GetCorrectedBoundBox(origin, bb, nFirstLine, nLastLine, nOrderX, nOrderY, pCoefs);
		   if(newBb.Height() > 5) {
				bb = newBb;
		   }
		   else {
			   nCoefs = nOrderX = nOrderY = 0;
		   }
	  }
  }
  bb.InflateRect(1, 1);

  int nTextHeight = pP->GetHeight();
  if(nTextHeight < 20 && (pDest == NULL || bb.Width() * bb.Height() <= nMaxDestBufSize * 2)){
	  nScale <<= 1;
  }
  if(pP->GetHeight() > 100){
	  nScale >>= 1;
  } 
  if(pScale)*pScale = nScale;
  nTextHeight = (nTextHeight * nScale) >> SCALE_FXPNT;

  int nWinSize = max(((nTextHeight * 3 / 2) & ~1) + 1, 15);

  nMargin = max(nMargin, ((nWinSize + 1) <<  (SCALE_FXPNT - 1)) / nScale + 1); 
  bb.InflateRect(nMargin, nMargin);
  int nScaledMinHeight = (nMinBitmapHeight << SCALE_FXPNT) / nScale;
  if(bb.Height() < nScaledMinHeight)bb.InflateRect(0, (nScaledMinHeight - bb.Height()) / 2); 
  int nSafeMargin = max((8 << SCALE_FXPNT) / nScale, 8);
  bb.right += (nSafeMargin - (bb.Width() % nSafeMargin)) % nSafeMargin;

  nBitmapWidth = (bb.Width() * nScale) >> SCALE_FXPNT;
  nBitmapHeight = (bb.Height() * nScale) >> SCALE_FXPNT;
  UCHAR *p1bppImg = NULL;
  if(bb.Width() > 16){
	  if(bFixCurvature){
		  if(nCoefs < 1){
			  nStatus = CANNOT_SUBDIVIDE_THE_PARAGRAPH;
			  p1bppImg = iri.GetRotatedRoi1bpp(p8bppImg, iri.nOrigWidth, iri.nOrigHeight, bb, nScale, nWinSize, nThreshold, 0, ppGreyImg, pDest, bWoB);
		  }
		  else{
			  p1bppImg = iri.GetRotatedRoi1bpp(p8bppImg, iri.nOrigWidth, iri.nOrigHeight, bb, nScale, origin, nOrderX, nOrderY, pCoefs,
				  nWinSize, nThreshold, 0, ppGreyImg, pDest, bWoB); 

		  }
	  }
	  else{
		  p1bppImg = iri.GetRotatedRoi1bpp(p8bppImg, iri.nOrigWidth, iri.nOrigHeight, bb, nScale, nWinSize, nThreshold, 0, ppGreyImg, pDest, bWoB);
	  }
	  CleanMargins(p1bppImg, nBitmapWidth, nBitmapHeight, (nMargin * nScale) >> SCALE_FXPNT);
#ifdef __VERBOSE
	  char sFilename[512];
	  sprintf(sFilename, "c:\\Data\\tmp\\chunks\\bmp_%s%d_%d.bmp", pP->GetBoW() ? "bow" : "wob", 1000 + nParagraph, nFirstLine);
	  SaveaBMP(p1bppImg, sFilename, nBitmapWidth, nBitmapHeight, 1, true, nBitmapWidth / 8);
	  sprintf(sFilename, "c:\\Data\\tmp\\chunks\\grey_%s%d_%d.bmp", pP->GetBoW() ? "bow" : "wob", 1000 + nParagraph, nFirstLine);
	  cvSaveImage(sFilename, *ppGreyImg);
#endif
	if(pOrderX)*pOrderX = nOrderX;
	if(pOrderY)*pOrderY = nOrderY;
	if(pBB) pBB->CopyRect(bb);
	if(pOrigin) *pOrigin = origin;
	return p1bppImg;
  }
  return NULL;
}

void CRawLines::Print(const char *fileName)const{
	FILE *fp = fopen(fileName, "wb");
	if(!fp) return;
	int nNoP = GetNumOfParagraphs();
	fprintf(fp, "Num of paragraphs = %d\r\n", nNoP);
	for(int i = 0; i < nNoP; ++i){
		const CParagraph *pP = m_vParagraphs[i];
		fprintf(fp, "Paragraph %d:\r\n", i);
		fprintf(fp, "left = %d\r\n", pP->left);
		fprintf(fp, "top = %d\r\n", pP->top);
		fprintf(fp, "right = %d\r\n", pP->right);
		fprintf(fp, "bottom = %d\r\n", pP->bottom);
	}
	fclose(fp);
}

float CRawLines::ReProcessParagraphs(const UCHAR *p8bppImg, const ImgRotationInfo &iri, bool bFixCurvature, COCR *pOcr, bool bUpSideDown){
	return 0;
	/*int nWordsFoundInDict = 0;
	int nTotalWords = 0;
	float fWordsInDictionaryRatio;
	for(int i = 0; i < m_vParagraphs.size(); ++i){
		m_vParagraphs[i]->CreateGreyImage(p8bppImg, iri, bFixCurvature, pOcr, bUpSideDown);
		nWordsFoundInDict += m_vParagraphs[i]->GetNumFoundInDictWords();
		nTotalWords += m_vParagraphs[i]->GetNumTotalWords();
	}
	if( nWordsFoundInDict == 0 )
		fWordsInDictionaryRatio = 0;
	if( nTotalWords == 0 )
		fWordsInDictionaryRatio = 1;

	fWordsInDictionaryRatio = (float)nWordsFoundInDict / (float)nTotalWords;
	return fWordsInDictionaryRatio;*/
}

BOOL CRawLines::GetPageDescription( int &nSections, int &nTotalWords, int &nTextAreaFrac,
								   int & nColumns, int & nHeaders) {	

	long nTotalWordArea = 0;
	nSections = nTotalWords = 0;
	nColumns = m_nColumns;
	nHeaders = m_nHeaders;
	for(int i = 0; i < m_vParagraphs.size(); ++i){	
		CParagraph *pP = m_vParagraphs[i];
		if(pP && (!pP->IsRunningIntoFrame() || pP->IsMainParagraph())
			&& (pP->GetArea() * 10 >= m_vParagraphs[m_nMainParagraphIndx]->GetArea()
				|| pP->Width() * 2 >= m_vParagraphs[m_nMainParagraphIndx]->Width())) {
			int nWords = m_vParagraphs[i]->GetNumTotalWords();
			if( nWords > 0 ) {
				nSections++;
				nTotalWords += nWords;
				nTotalWordArea += (m_vParagraphs[i]->right - m_vParagraphs[i]->left)*(m_vParagraphs[i]->bottom - m_vParagraphs[i]->top );

			}
		}
	}

	nTextAreaFrac = (nTotalWordArea * 100) / (0x800*0x600);

	return TRUE;
}

/////////////////////////////CRawLine////////////////////////////////////////////////////
bool CRawLine::GetConnectionPoints(vector<pair<int, pair<int, int> > > & vPoints, int &nAvgTop, int &nAvgBottom)const{
	if(size() < 5) return false;
	int nCount = 0, nAvgH = 0;
	nAvgTop = nAvgBottom = 0;
	const_iterator i;
	for(i = begin() + 1; i != end(); ++i){
		int nTop = max(i->top, (i-1)->top);
		int nBottom = min(i->bottom, (i-1)->bottom);
		nAvgH += nBottom - nTop;
		++nCount;
	}
	if(nCount > 0){
		nAvgH = (nAvgH + nCount / 2) / nCount;
	}
	else{
		return false;
	}
	nCount = 0;
	for(i = begin() + 1; i != end(); ++i){
		int nTop = max(i->top, (i-1)->top);
		int nBottom = min(i->bottom, (i-1)->bottom);
		nAvgTop += nTop;
		nAvgBottom += nBottom;
		++nCount;
		int nX = (i->left + (i-1)->right) / 2;
		int nH = nBottom - nTop;
		if(nH * 2 >= nAvgH && nH <= nAvgH * 2){
			vPoints.push_back(pair <int, pair<int, int> >(nX, pair<int, int>(nTop, nBottom)));
		}
	}
	int nEndCount = 0;
	for(i = begin(); nEndCount < 1 && i != end(); ++i, ++nEndCount){
		nAvgTop += i->top;
		nAvgBottom += i->bottom;
		++nCount;
		int nX = i->left;
		int nH = i->bottom - i->top;
		if(nH * 2 >= m_nAvrgHeight && m_nAvrgHeight * 2 >= nH){
			vPoints.push_back(pair <int, pair<int, int> >(nX, pair<int, int>(i->top, i->bottom)));
		}
	}
	nEndCount = 0;
	for(const_reverse_iterator j = rbegin(); nEndCount < 1 && j != rend(); ++j, ++nEndCount){
		nAvgTop += j->top;
		nAvgBottom += j->bottom;
		++nCount;
		int nX = (j)->right;
		int nH = j->bottom - j->top;
		if(nH * 2 >= m_nAvrgHeight && m_nAvrgHeight * 2 >= nH){
			vPoints.push_back(pair <int, pair<int, int> >(nX, pair<int, int>(j->top, j->bottom)));
		}
	}
	if(nCount <= 0) return false;
	nAvgTop /= nCount;
	nAvgBottom /= nCount;
	return true;
}

bool CRawLine::GetReferencePoints(CRect roi, vector<CPoint> & vXY, vector<int> & vY){
	int nAvgTop, nAvgBottom;
	vector<pair<int, pair<int, int> > > vConnectionPoints;
	if(!GetConnectionPoints(vConnectionPoints, nAvgTop, nAvgBottom)){
		return false;
	}
	CPoint center(roi.CenterPoint());
	nAvgTop -= center.y;
	nAvgBottom -= center.y;
	for(vector<pair<int, pair<int, int> > >::const_iterator i = vConnectionPoints.begin(); i != vConnectionPoints.end(); ++i){
		vXY.push_back(CPoint(i->first - center.x, nAvgTop));
		vY.push_back(i->second.first - center.y);
		vXY.push_back(CPoint(i->first - center.x, nAvgBottom));
		vY.push_back(i->second.second - center.y);
	}
	return true;
}

bool CRawLine::GetReferencePoints(const CPoint & origin, vector<CPoint> & vXY, vector<int> & vY,
								  int *pAvgTopPos)const{
	int nAvgTop, nAvgBottom;
	vector<pair<int, pair<int, int> > > vConnectionPoints;
	if(!GetConnectionPoints(vConnectionPoints, nAvgTop, nAvgBottom)){
		return false;
	}

	if(pAvgTopPos) {
		int nH = nAvgBottom - nAvgTop;
		nAvgTop = *pAvgTopPos;
		nAvgBottom = nAvgTop + nH;
	}
	nAvgTop -= origin.y;
	nAvgBottom -= origin.y;
	for(vector<pair<int, pair<int, int> > >::const_iterator i = vConnectionPoints.begin(); i != vConnectionPoints.end(); ++i){
		vXY.push_back(CPoint(i->first - origin.x, nAvgTop));
		vY.push_back(i->second.first - origin.y);
		vXY.push_back(CPoint(i->first - origin.x, nAvgBottom));
		vY.push_back(i->second.second - origin.y);
	}
	return true;
}

int GetSpineY(int nX, vector<CPoint>::const_iterator pos);

int CRawLine::GetAverageDistance(const CRawLine *pRl) const {
	int nAvg = 0, nLength = 0;
	for(vector<CPoint>::const_iterator i = m_vSpine.begin() + 1, j = pRl->m_vSpine.begin() + 1;
		i != m_vSpine.end() && j != pRl->m_vSpine.end();) {
			int nXStart = max((i-1)->x, (j-1)->x);
			int nXEnd = min(i->x, j->x);
			if(nXStart < nXEnd) {
				int nS = GetSpineY(nXStart, j - 1) + GetSpineY(nXEnd, j - 1) -
					GetSpineY(nXStart, i - 1) - GetSpineY(nXEnd, i - 1);
				int nD = nXEnd - nXStart;
				nAvg += nS * nD;
				nLength += nD;
			}
			if(i->x < j->x) {
				++i;
			}
			else {
				++j;
			}
	}
	return (nLength > 0) ? nAvg / (2 * nLength) : -1;
}

void RotateUpsideDown(vector<CRect> & vOcredLine, int nWidth, int nHeight){
	if(vOcredLine.empty()) {
		return;
	}
	reverse(vOcredLine.begin(), vOcredLine.end());
	CRect tmp ;
	for(vector<CRect>::iterator it = vOcredLine.begin(); it != vOcredLine.end(); ++it) {
		tmp.bottom = nHeight - 1 - it->top;
		tmp.top = nHeight - 1 - it->bottom;
		tmp.right = nWidth - 1 - it->left;
		tmp.left = nWidth - 1 - it->right;
		it->CopyRect(tmp);
	}
}

int FromCorrectedY2OriginalY(int nX, int nCorrectedY, int nXCenter, int nYCenter, const LONGLONG *pCoefs, 
							LONGLONG (*GetValueFunction)(LONGLONG, LONGLONG, const LONGLONG *)) {
	if(!GetValueFunction) return nCorrectedY - nYCenter;
	return int(GetValueFunction(nX - nXCenter, nCorrectedY - nYCenter, pCoefs) >> CURVATURE_RES_SHIFT) ;
}

int CRawLine::CalcOverlap(const vector<CRect> & vRects) const {
	int nArea = 0;
	CRect rect;
	for(const_iterator i = begin(), j = vRects.begin(); i != end() && j != vRects.end();) {
		rect.IntersectRect(*i, *j);
		if(!rect.IsRectEmpty()){
			nArea += rect.Width() * rect.Height();
		}
		if(j->right < i->right){
			++j;
		}
		else {
			++i;
		}
	}
	return nArea;
}

bool CompareForceSingle(const CRawLine *pL1, const CRawLine *pL2) {
	if(XOverlap(*pL1, *pL2) <= 0) {
		if(pL1->left < pL2->left) {
			if(abs(pL1->m_vSpine.back().y - pL2->m_vSpine.front().y ) * 2 < pL1->GetHeight() + pL2->GetHeight()) { 
				return true;
			}
		}
		else if(abs(pL1->m_vSpine.front().y - pL2->m_vSpine.back().y ) * 2 < pL1->GetHeight() + pL2->GetHeight()) { 
			return false;
		}
	}
	return pL1->top < pL2->top;
}

BOOL CRawLines::SetForceSingleOrderAndRegularOrder() {
	m_vRegularOrder.clear();
	m_vForceSingleOrder.clear();
	for(vector<CParagraph*>::iterator i = m_vParagraphs.begin(); i != m_vParagraphs.end(); ++i) {
		if(*i) {
			if((*i)->IsMainParagraph() || !(*i)->IsRunningIntoFrame()) {
				for(vector<CRawLine*>::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
					if(*j) {
						m_vRegularOrder.push_back(*j);
					}
				}
			}
			else {
				delete *i;
				*i = NULL;
			}
		}
	}
	if(m_vRegularOrder.empty()) {
		return FALSE;
	}
	m_vForceSingleOrder.assign(m_vRegularOrder.begin(), m_vRegularOrder.end());
	sort(m_vForceSingleOrder.begin(), m_vForceSingleOrder.end(), CompareForceSingle);
	return TRUE;
}

bool CRawLines::NoText() const{
	for(int i = 0; i< m_vParagraphs.size(); ++i) {
		if(m_vParagraphs[i] && !m_vParagraphs[i]->NoText()) {
			return false;
		}
	}
	return true;
}

int CRawLine::CountWords()const {
	int nMinWordSpace = (1 << 8) / 3;
	if(size() < 1) return 0;
	if(size() == 1) return 1;
	int nWords = 1;
	for(const_iterator i = begin() + 1; i != end(); ++i){
		if(i->left - (i-1)->right > ((nMinWordSpace * m_nAvrgHeight) >> 8)){
			++nWords;
		}
	}
	return nWords;
}

void CRawLines::SetUpSideDown(BOOL bUpSideDown) {
	if(bUpSideDown && !m_bUpSideDown) {
		m_bUpSideDown = bUpSideDown;
		reverse(m_vRegularOrder.begin(), m_vRegularOrder.end());
		reverse(m_vForceSingleOrder.begin(), m_vForceSingleOrder.end());
	}
}

CRawLine *CtextLine2CRowLine(const CTextLine *pTextLine){
	if(!pTextLine) return NULL;
	CRawLine *pRl = new CRawLine; 
	pRl->CopyRect(*pTextLine);
	pRl->m_nAvrgHeight = pTextLine->GetHeight();
	pRl->m_bIsSalient = pTextLine->IsSalient();
	pRl->m_nTouchingFrame = 0;
	pRl->m_nAvgBackGrnd = pTextLine->GetAvgBackGrnd();
	pRl->m_nAvgGrey = pTextLine->GetAvgGrey();
	const BaseElemSet & setCC = pTextLine->GetSetCC();
	for(BaseElemSet::const_iterator i = setCC.begin(); i != setCC.end(); ++i){
		pRl->push_back(*i);
	}
	pTextLine->GetSpine(pRl->m_vSpine);
	return pRl;
}

void Copy1BitRectangle(const UCHAR *pSrc, int nSrcWidth, CRect srcRect, UCHAR *pDest, int nDestWidth, CPoint destLeftTop){
	pSrc += ((srcRect.top * nSrcWidth + srcRect.left ) >> 3);
	UCHAR srcMask = 0x80 >> (srcRect.left & 0x7);
	pDest += ((destLeftTop.y * nDestWidth + destLeftTop.x ) >> 3);
	UCHAR destMask = 0x80 >> (destLeftTop.x & 0x7);
	const UCHAR *pS;
	UCHAR *pD, dM, sM;
	for(int i = 0; i<srcRect.Height(); ++i){
		pS = pSrc;
		pD = pDest;
		dM = destMask;
		sM = srcMask;
		for(int j=0; j<srcRect.Width(); ++j){
			if(sM == 0){
				sM = 0x80;
				++pS;
			}
			if(dM == 0){
				dM = 0x80;
				++pD;
			}
			if((*pS & sM) == 0){
				*pD &= ~dM;
			}
			dM >>= 1;
			sM >>= 1;
		}
		pSrc += (nSrcWidth >> 3);
		pDest += (nDestWidth >> 3);
	}
}


void CRawLine::Copy(const UCHAR *pSrc, int nSrcWidth, UCHAR *pDest, int nDestWidth, CPoint shift)const{
	for(const_iterator i = begin(); i != end(); ++i){
		Copy1BitRectangle(pSrc, nSrcWidth, *i, pDest, nDestWidth, i->TopLeft() + shift);
	}
}

int CopyTextLines2RawLines(const CTextLines &tl, CRawLines & lines){
	lines.clear();
	const vector <CTextRegion*> *pRegions = tl.GetRegions(); 
	int nCount = 0, nSumHeight = 0;
	for(vector<CTextRegion*>::const_iterator i = pRegions->begin(); i != pRegions->end(); ++i){
		if(g_bCancelProcessing) return -1;
		if(*i && (*i)->size() > 0/*&& (*i)->IsSalient()*/){
			CParagraph *pP = new CParagraph(lines.m_vParagraphs.size(), (*i)->GetBoW(), (*i)->IsEmbedded());
			for(CTextRegion::const_iterator j = (*i)->begin(); j != (*i)->end(); ++j){
				if(*j){
					CRawLine *pRl = CtextLine2CRowLine(*j);
					if(pRl){
						pP->AddRawLine(pRl);
						lines.push_back(pRl);
						++nCount;
						nSumHeight += (*j)->GetHeight();
					}
				}
			}
			lines.m_vParagraphs.push_back(pP);
		}
	}
	return (nCount > 0) ? nSumHeight / nCount : 0;
}

int LocateTextLines(const UCHAR *p8bppImg, const UCHAR* p1bppBoW, const UCHAR* p1bppWoB, 
					const ImgRotationInfo &iri, int nBverWThreshold, CRawLines& lines, 
					int & nActualAngle){
	CTextLines *pTl = LocateTextLines(p8bppImg, p1bppBoW, p1bppWoB, iri, nBverWThreshold, 
					 nActualAngle);
	int nRet = CopyTextLines2RawLines(*pTl, lines);
	delete pTl;
	return nRet;
}
