1 //
2 // C++ Implementation: fmlayout
3 //
4 // Description:
5 //
6 //
7 // Author: Pierre Marchand <pierremarc@oep-h.com>, (C) 2008
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12 #include "fmlayout.h"
13 #include "fontitem.h"
14 #include "shortcuts.h"
15 #include "fmlayoptwidget.h"
16 #include "typotek.h"
17 #include "textprogression.h"
18 
19 #include <cstdlib>
20 
21 #include <QDialog>
22 #include <QGridLayout>
23 #include <QString>
24 #include <QGraphicsScene>
25 #include <QGraphicsView>
26 #include <QGraphicsProxyWidget>
27 // #include <QProgressBar>
28 #include <QDebug>
29 #include <QApplication>
30 #include <QTime>
31 #include <QAction>
32 #include <QMenu>
33 // #include <QMutexLocker>
34 #include <QMutex>
35 // #include <QWaitCondition>
36 #include <QCoreApplication>
37 #include <QGraphicsObject>
38 
39 #define OUT_OF_RECT 99999999.0
40 
41 int fm_layout_total_nod_dbg;
42 int fm_layout_total_skip_nod_dbg;
43 int fm_layout_total_leaves_dbg;
44 
ListItem()45 Node::ListItem::ListItem() :n ( 0 ), distance ( 0.0 )
46 {
47 	// 	qDebug()<<"CV empty";
48 }
49 
ListItem(Node * N,double D)50 Node::ListItem::ListItem ( Node * N, double D ) :n ( N ),distance ( D )
51 {
52 	// 	qDebug()<<"CV n d"<<n->index<<distance;
53 }
54 
~ListItem()55 Node::ListItem::~ListItem()
56 {
57 	// 	qDebug()<<"~ListItem"<< n<<n->index;
58 	if(n)
59 	{
60 		delete n;
61 		n = 0;
62 	}
63 }
64 
Node(FMLayout * layoutEngine,int i)65 Node::Node (FMLayout * layoutEngine,  int i )
66 	:lyt(layoutEngine),
67 	index ( i )
68 {
69 	// 	if(index == 0)
70 	// 		qDebug()<<"+N"<< this<<index << ++fm_layout_total_nod_dbg;
71 }
72 
hasNode(int idx)73 bool Node::hasNode ( int idx )
74 {
75 	for ( int i ( 0 ); i < nodes.count(); ++i )
76 	{
77 		if ( nodes[i]->n->index == idx )
78 			return true;
79 	}
80 	return false;
81 }
82 
nodes_clear()83 void Node::nodes_clear()
84 {
85 	// 	if(index == 0)
86 	// 		qDebug()<<"-C"<<this<< nodes.count();
87 	while (!nodes.isEmpty())
88 		delete nodes.takeFirst();
89 }
90 
nodes_insert(ListItem * v)91 void Node::nodes_insert(ListItem * v)
92 {
93 
94 
95 	if ( nodes.isEmpty() )
96 	{
97 		nodes.insert ( 0, v );
98 	}
99 	else
100 	{
101 		bool ist(false);
102 		for ( int nI ( 0 );nI<nodes.count();++nI )
103 		{
104 			if ( v->distance <= nodes[nI]->distance )
105 			{
106 				nodes.insert ( nI,v );
107 				ist = true;
108 				break;
109 			}
110 		}
111 		if(!ist)
112 			nodes.append(v);
113 	}
114 	// 	qDebug()<<"+I"<<this <<v->n->index<<nodes.count();
115 }
116 
~Node()117 Node::~Node()
118 {
119 
120 	// 	qDebug()<<"~N"<<this<< deepCount() << --fm_layout_total_nod_dbg;
121 	nodes_clear();
122 }
123 
deepCount()124 int Node::deepCount()
125 {
126 	int c ( nodes.count() );
127 	for ( int i ( 0 ); i < c ; ++i )
128 	{
129 		c += nodes[i]->n->deepCount();
130 	}
131 	return c;
132 }
133 
sPath(double dist,QList<int> curList,QList<int> & theList,double & theScore)134 void Node::sPath ( double dist , QList< int > curList, QList< int > & theList, double & theScore )
135 {
136 	// 	QList<int> debugL;
137 	// 	foreach(ListItem v, nodes)
138 	// 	{debugL << v.n->index;}
139 	// 	qDebug()<<"Node::sPath(" <<dist<< ", "<<curList<<", "<<theList<<", "<<theScore<<")"<< "I L"<<index<<debugL;
140 	int deep ( curList.count() + 1 );
141 	//	FMLayout* lyt ( FMLayout::getLayout() );
142 	if ( lyt->stopIt )
143 	{
144 		theScore = 0;
145 	}
146 	// cIdx is first glyph of the line
147 	int cIdx ( index );
148 	// bIndex is the break which sits on cIdx
149 	int bIndex ( lyt->breakList.indexOf ( cIdx ) );
150 	bool wantPlus ( true );
151 	while ( wantPlus )
152 	{
153 		++bIndex;
154 		if ( bIndex  < lyt->breakList.count() )
155 		{
156 			// additive width of glyphs
157 			double di ( lyt->distance ( cIdx, lyt->breakList[bIndex],lyt->theString ) );
158 			if ( di >= lyt->lineWidth ( deep ) )
159 			{
160 				/// BEFORE
161 				for ( int backIndex ( 1 ) ; ( bIndex - backIndex > 0 ) && ( lyt->breakList[bIndex - backIndex] != cIdx ); ++backIndex )
162 				{
163 					int soon ( lyt->breakList[bIndex - backIndex] );
164 					double needWidth(lyt->distance ( cIdx, soon ,lyt->theString ));
165 					double needWidthStripped(lyt->distance ( cIdx, soon ,lyt->theString , true ) );
166 					double spaceWidth(needWidth - needWidthStripped);
167 
168 					double disN = needWidth - lyt->lineWidth ( deep ) ;
169 
170 					double compressValue( disN * 100.0 / spaceWidth  );
171 					// 					qDebug()<<"PRE spaceWidth("<<spaceWidth<<") disN("<<disN<<") compressValue("<<compressValue<<")";
172 					if(compressValue > lyt->FM_LAYOUT_MAX_COMPRESSION)
173 					{
174 						break;
175 					}
176 					// 					else
177 					// 						qDebug()<<"["<<cIdx<<","<<soon<<"]spaceWidth("<<spaceWidth<<") disN("<<disN<<") compressValue("<<compressValue<<")";
178 
179 
180 					Node* sN = 0;
181 					sN = new Node (lyt, soon );
182 					if ( lyt->hyphenList.contains ( soon ) )
183 						disN *= lyt->FM_LAYOUT_HYPHEN_PENALTY;
184 
185 					Node::ListItem* vN = new Node::ListItem( sN, qAbs ( disN * lyt->FM_LAYOUT_NODE_SOON_F ) );
186 					nodes_insert(vN);
187 					if ( QChar ( lyt->theString[soon].lChar ).category() == QChar::Separator_Space )
188 						break;
189 				}
190 				/// AT CLOSEST BREAK (after)
191 				{
192 					int fit ( lyt->breakList[bIndex] );
193 
194 					double needWidth(lyt->distance ( cIdx, fit,lyt->theString ));
195 					double needWidthStripped(lyt->distance ( cIdx, fit ,lyt->theString , true ) );
196 					double spaceWidth(needWidth - needWidthStripped);
197 
198 					double disF =  needWidth - lyt->lineWidth ( deep );
199 
200 					double compressValue( disF * 100.0 / spaceWidth  );
201 					if(compressValue <= lyt->FM_LAYOUT_MAX_COMPRESSION)
202 					{
203 						// 						qDebug()<<"["<<cIdx<<","<<fit<<"]("<< lyt->sepCount(cIdx, fit ,lyt->theString) <<") spaceWidth("<<spaceWidth<<") disF("<<disF<<") compressValue("<<compressValue<<")";
204 
205 						Node* sF = 0;
206 						sF = new Node (lyt,  fit );
207 						if ( lyt->hyphenList.contains ( fit ) )
208 							disF *= lyt->FM_LAYOUT_HYPHEN_PENALTY;
209 
210 						Node::ListItem* vF = new Node::ListItem ( sF,qAbs ( disF * lyt->FM_LAYOUT_NODE_FIT_F ) );
211 						// 				curNode->nodes << vF;
212 						nodes_insert ( vF );
213 
214 					}
215 				}
216 				/// AFTER
217 				for ( int nextIndex ( 1 ); bIndex + nextIndex <  lyt->breakList.count() ; ++nextIndex )
218 				{
219 					int late ( lyt->breakList[bIndex + nextIndex] );
220 					double needWidth(lyt->distance ( cIdx, late ,lyt->theString ));
221 					double needWidthStripped(lyt->distance ( cIdx, late ,lyt->theString , true ) );
222 					double spaceWidth(needWidth - needWidthStripped);
223 
224 					double disL = needWidth - lyt->lineWidth ( deep );
225 
226 					double compressValue(  disL * 100.0 / spaceWidth  );
227 					if(compressValue > lyt->FM_LAYOUT_MAX_COMPRESSION)
228 					{
229 						// 						qDebug()<<"break_ cV ="<<compressValue;
230 						break;
231 					}
232 					// 					else
233 					// 						qDebug()<<"["<<cIdx<<","<<late<<"]spaceWidth("<<spaceWidth<<") disL("<<disL<<") compressValue("<<compressValue<<")";
234 
235 					Node* sL = 0;
236 					sL = new Node (lyt,  late );
237 					if ( lyt->hyphenList.contains ( late ) )
238 						disL *= lyt->FM_LAYOUT_HYPHEN_PENALTY;
239 
240 					Node::ListItem* vL = new Node::ListItem ( sL, qAbs ( disL * lyt->FM_LAYOUT_NODE_LATE_F ) );
241 					nodes_insert ( vL );
242 
243 					if ( late < lyt->theString.count() && QChar ( lyt->theString[late].lChar ).category() == QChar::Separator_Space )
244 						break;
245 				}
246 
247 				wantPlus = false;
248 
249 			}
250 			else if ( lyt->lineWidth ( deep ) == OUT_OF_RECT )
251 			{
252 				wantPlus = false;
253 			}
254 		}
255 		else // end of breaks list
256 		{
257 			// 			qDebug()<<"END OF BREAKS";
258 			int soon ( lyt->breakList[bIndex - 1] );
259 			if ( soon != cIdx && !hasNode ( soon ) )
260 			{
261 
262 				Node* sN = 0;
263 				sN = new Node (lyt,  soon );
264 				double disN = lyt->lineWidth ( deep ) - lyt->distance ( cIdx, soon,lyt->theString );
265 
266 				Node::ListItem* vN = new Node::ListItem ( sN, qAbs ( disN * lyt->FM_LAYOUT_NODE_END_F ) );
267 				// 				curNode->nodes << vN;
268 				nodes_insert ( vN );
269 			}
270 
271 			wantPlus = false;
272 		}
273 	}
274 
275 	// 	qDebug()<<"N"<<nodes.count();
276 	bool isLeaf ( nodes.isEmpty() );
277 	curList << index ;//(isLeaf ? index-1 : index);
278 
279 	// 	double dCorrection ( ( double ) theList.count() / ( double ) curList.count() );
280 	// 	dCorrection = 1.0;
281 	// 	qDebug()<<"COR tl.c cl.c"<<dCorrection<<theList.count()<<curList.count();
282 	while ( !nodes.isEmpty() )
283 	{
284 		// 		ListItem v = nodes.first() ;
285 		double d1 ( dist + nodes[0]->distance / curList.count() /** dCorrection*/ );
286 		double d2 ( theScore / qMax ( 1.0, ( double ) theList.count() ) );
287 		if ( d1  <  d2 )
288 		{
289 			nodes[0]->n->sPath ( dist + nodes[0]->distance, curList, theList, theScore );
290 		}
291 		else
292 			++fm_layout_total_skip_nod_dbg;
293 		delete nodes.takeFirst();
294 		// 		nodes.removeFirst();
295 	}
296 
297 	if ( isLeaf )
298 	{
299 		double mDist ( dist / deep );
300 		double mScore ( theScore / theList.count() );
301 		// 		qDebug() <<"D S N"<< mDist << mScore << curList;
302 		if ( mDist < mScore )
303 		{
304 			theScore = dist;
305 			theList = curList;
306 		}
307 		++fm_layout_total_leaves_dbg;
308 	}
309 }
310 
311 //FMLayout *FMLayout::instance = 0;
FMLayout(QGraphicsScene * scene,FontItem * font,QRectF rect)312 FMLayout::FMLayout ( QGraphicsScene * scene, FontItem * font , QRectF rect )
313 	:theScene(scene),
314 	theFont(font),
315 	layoutIsFinished(true),
316 	contextIsMainThread(true)
317 {
318 	if(rect.isNull())
319 	{
320 		QRectF tmpRect = theScene->sceneRect();
321 		double sUnitW ( tmpRect.width() * .1 );
322 		double sUnitH ( tmpRect.height() * .1 );
323 
324 		theRect.setX ( 2.0 * sUnitW );
325 		theRect.setY ( 1.0 * sUnitH );
326 		theRect.setWidth ( 6.0 * sUnitW );
327 		theRect.setHeight ( 8.0 * sUnitH );
328 	}
329 	else
330 		theRect = rect;
331 	rules = new QGraphicsRectItem;
332 	node = 0;
333 	//	layoutMutex = new QMutex;
334 	optionHasChanged = true;
335 	persistentScene = false;
336 
337 	// 	progressBar = new QProgressBar ;
338 	// 	onSceneProgressBar = new QGraphicsProxyWidget ;
339 	// 	onSceneProgressBar->setWidget ( progressBar );
340 	// 	onSceneProgressBar->setZValue ( 1000 );
341 
342 	//	connect ( this, SIGNAL ( paragraphFinished() ), this, SLOT( endOfParagraph() ) );
343 	//	connect ( this, SIGNAL ( layoutFinished() ), this, SLOT ( doDraw() ) );
344 	//	connect ( this, SIGNAL ( paintFinished() ), this, SLOT ( endOfRun() ) );
345 
346 	FM_LAYOUT_NODE_SOON_F=	1200.0;
347 	FM_LAYOUT_NODE_FIT_F=	1000.0;
348 	FM_LAYOUT_NODE_LATE_F=	2000.0;
349 	FM_LAYOUT_NODE_END_F=	2500.0;
350 	FM_LAYOUT_HYPHEN_PENALTY = 1.5;
351 	FM_LAYOUT_MAX_COMPRESSION = 50.0; // 50%
352 
353 	optionDialog = new QWidget;
354 	//	optionDialog->setWindowTitle ( tr ( "Text engine options" ) );
355 	optionLayout =  new QGridLayout(optionDialog) ;
356 
357 	optionsWidget = new FMLayOptWidget;
358 	optionsWidget->setRange ( FMLayOptWidget::BEFORE, 1, 10000 );
359 	optionsWidget->setRange ( FMLayOptWidget::EXACT, 1, 10000 );
360 	optionsWidget->setRange ( FMLayOptWidget::AFTER, 1, 10000 );
361 	optionsWidget->setRange ( FMLayOptWidget::END, 1, 10000 );
362 	optionsWidget->setRange ( FMLayOptWidget::HYPHEN, 1, 100 );
363 	optionsWidget->setRange ( FMLayOptWidget::SPACE, 1, 100 );
364 
365 	optionsWidget->setValue ( FMLayOptWidget::BEFORE,FM_LAYOUT_NODE_SOON_F );
366 	optionsWidget->setValue ( FMLayOptWidget::EXACT,FM_LAYOUT_NODE_FIT_F );
367 	optionsWidget->setValue ( FMLayOptWidget::AFTER,FM_LAYOUT_NODE_LATE_F );
368 	optionsWidget->setValue ( FMLayOptWidget::END,FM_LAYOUT_NODE_END_F );
369 	optionsWidget->setValue ( FMLayOptWidget::HYPHEN,FM_LAYOUT_HYPHEN_PENALTY * 10 );
370 	optionsWidget->setValue ( FMLayOptWidget::SPACE,FM_LAYOUT_MAX_COMPRESSION );
371 
372 	optionLayout->addWidget(optionsWidget,0,0);
373 
374 //	connect ( optionsWidget,SIGNAL ( valueChanged ( int ) ),this,SLOT ( slotOption ( int ) ) );
375 	connect(this, SIGNAL(objectWanted(QObject*)), typotek::getInstance(), SLOT(pushObject(QObject*)));
376 
377 }
378 
~FMLayout()379 FMLayout::~ FMLayout()
380 {
381 	if(optionDialog)
382 		delete optionDialog;
383 }
384 
385 //FMLayout * FMLayout::getLayout()
386 //{
387 //	if(!instance)
388 //	{
389 //		instance = new FMLayout;
390 //		Q_ASSERT(instance);
391 //	}
392 //	return instance;
393 //}
394 
run()395 void FMLayout::run()
396 {
397 //	if(justRedraw)
398 //		doDraw();
399 //	else
400 	{
401 		for ( int i ( 0 ); i < paragraphs.count() ; ++ i )
402 		{
403 			// 		qDebug()<<"Oy Rb"<<origine.y()<<theRect.bottom();
404 			if ( origine.y() > theRect.bottom() )
405 				break;
406 			theString = paragraphs[i];
407 			if ( theString.isEmpty() )
408 				continue;
409 			// 			node = new Node ( 0 );
410 			doGraph();
411 			clearCaches();
412 			{
413 				// Debug output
414 				fm_layout_total_leaves_dbg = 0;
415 				fm_layout_total_nod_dbg = 0;
416 				fm_layout_total_skip_nod_dbg = 0;
417 			}
418 
419 			doLines();
420 
421 			// 			qDebug() <<"NODES LEAVES SKIP"<<fm_layout_total_nod_dbg<<fm_layout_total_leaves_dbg<<fm_layout_total_skip_nod_dbg;
422 			// 			if ( node )
423 			// 			{
424 			// 				delete node;
425 			// 				node = 0;
426 			// 			}
427 			clearCaches();
428 			breakList.clear();
429 			hyphenList.clear();
430 			// 			emit paragraphFinished ( i + 1 );
431 			emit paragraphFinished();
432 
433 		}
434 		doDraw();
435 	}
436 	qDebug()<<"\tLayout Finished";
437 }
438 
doLayout(const QList<GlyphList> & spec,double fs,FontItem * font)439 void FMLayout::doLayout ( const QList<GlyphList> & spec , double fs, FontItem* font)
440 {
441 	qDebug()<<"FMLayout::doLayout"<<thread();
442 	if(font)
443 		theFont = font;
444 	stopIt = false;
445 	layoutIsFinished = false;
446 
447 	TextProgression *tp = TextProgression::getInstance();
448 
449 	if ( tp->inLine() == TextProgression::INLINE_LTR )
450 		origine.rx() = theRect.left() ;
451 	else if ( tp->inLine() == TextProgression::INLINE_RTL )
452 		origine.rx() = theRect.right() ;
453 	else if ( tp->inLine() == TextProgression::INLINE_BTT )
454 		origine.ry() = theRect.bottom();
455 	else if ( tp->inLine() == TextProgression::INLINE_TTB )
456 		origine.ry() = theRect.top();
457 
458 	if ( tp->inBlock() == TextProgression::BLOCK_TTB )
459 		origine.ry() = theRect.top();
460 	else if ( tp->inBlock() == TextProgression::BLOCK_RTL )
461 		origine.rx() = theRect.right();
462 	else if ( tp->inBlock() == TextProgression::BLOCK_LTR )
463 		origine.rx() = theRect.left();
464 
465 	// 	qDebug()<<"LO"<<lastOrigine<<"O"<<origine<<"options"<<optionHasChanged;
466 //	if( !optionHasChanged && origine == lastOrigine && fontSize == fs && paragraphs == spec )
467 //	{
468 //		justRedraw = true;
469 //		//		typotek::getInstance()->startProgressJob( lines.count() );
470 //		// 		qDebug()<<"LAYOUT O : lines "<<lines.count();
471 //	}
472 //	else
473 	{
474 		// 		qDebug()<<"LAYOUT 1";
475 		justRedraw = false;
476 		lines.clear();
477 		//		typotek::getInstance()->startProgressJob( paragraphs.count() + ( theRect.height() / fs*1.20 ) );// layout AND draw
478 	}
479 	lastOrigine = origine;
480 	fontSize = fs;
481 	paragraphs = spec;
482 	optionHasChanged = false;
483 
484 	run();
485 	layoutIsFinished = true;
486 	emit layoutFinished();
487 	qDebug()<< "FMLayout::doLayout return" << justRedraw;
488 }
489 
endOfRun()490 void FMLayout::endOfRun()
491 {
492 	// 	qDebug() <<"FMLayout::endOfRun()";
493 	// 	theScene->removeItem ( onSceneProgressBar );
494 	// 	disconnect ( this,SIGNAL ( paragraphFinished ( int ) ),progressBar,SLOT ( setValue ( int ) ) );
495 	// 	if ( node )
496 	// 	{
497 	// 		delete node;
498 	// 		node = 0;
499 	// 	}
500 	// 	qDebug()<<"EOR A"<<lines.count();
501 	//	layoutMutex->unlock();
502 	if ( stopIt ) // We’re here after a interruption
503 	{
504 		stopIt = false;
505 		//		typotek::getInstance()->endProgressJob();
506 		emit updateLayout();
507 	}
508 	//	else
509 	//		typotek::getInstance()->endProgressJob();
510 
511 	// 	qDebug()<<"EOR B"<<lines.count();
512 }
513 
stopLayout()514 void FMLayout::stopLayout()
515 {
516 	stopIt = true;
517 	emit clearScene();
518 }
519 
doGraph()520 void FMLayout::doGraph() // Has became doBreaks
521 {
522 	// 	qDebug() <<"FMLayout::doGraph()";
523 	QTime t;
524 	t.start();
525 	/**
526 	I hit a power issue with my graph thing as in its initial state.
527 	So, I’ll try now to cut the tree as it’s built, not far than what’s done in chess programs.
528 
529 	*/
530 	// At first we’ll provide a very simple implementation to test things out
531 
532 	// A) where can we break?
533 	breakList.clear();
534 	hyphenList.clear();
535 
536 	for ( int a ( 0 ) ; a < theString.count() ; ++a )
537 	{
538 		if ( QChar ( theString[a].lChar ).category() == QChar::Separator_Space )
539 			breakList << a+1;
540 		if ( theString[a].isBreak )
541 		{
542 			breakList << a+1;
543 			hyphenList << a+1;
544 		}
545 	}
546 	breakList << theString.count();
547 	// 	qDebug() <<"BREAKS"<<breakList.count();
548 	// 	qDebug() <<"HYPHENS"<<hyphenList.count();
549 	// 	qDebug() <<"doGraph T(ms)"<<t.elapsed();
550 }
551 
doLines()552 void FMLayout::doLines()
553 {
554 	// 	qDebug() <<"FMLayout::doLines()";
555 	QTime t;
556 	t.start();
557 	// Run through the graph and find the shortest path
558 	indices.clear();
559 	double score ( INFINITE );
560 	Node locNode(this, 0);
561 	locNode.sPath ( 0,QList<int>(),indices,score );
562 	// 	qDebug()<<"================================";
563 	// 	qDebug()<< &locNode << locNode.nodes.count() << locNode.deepCount();
564 	// 	qDebug()<<"================================";
565 
566 
567 	clearCaches();
568 	// la messe est dite ! :-)
569 	// 	qDebug() <<"S I"<<score<<indices;
570 
571 	int startLine ( lines.count() );
572 
573 	int maxIndex ( indices.count() - 1 );
574 	// 	qDebug()<<"SC IC"<<theString.count()<<indices.count();
575 	bool hasHyph ( false );
576 	GlyphList curHyph;
577 
578 	if(theString.isEmpty())
579 		return;
580 
581 	for ( int lIdx ( 0 ); lIdx < maxIndex ; ++lIdx )
582 	{
583 		if ( stopIt || ((adjustedSampleInter * (lines.count() + 1)) > theRect.height()))
584 			break;
585 		int start1 ( /*!lIdx ?*/ indices[lIdx] /*: indices[lIdx] + 1*/ );
586 		int end1 ( indices[ lIdx + 1 ] );
587 
588 		GlyphList inList ( theString.mid ( start1 , end1 - start1 /*+ 1 */ ) );
589 
590 		if(inList.isEmpty())
591 			continue;
592 		if ( QChar ( inList.first().lChar ).category() == QChar::Separator_Space )
593 		{
594 			while ( (!inList.isEmpty()) && (QChar ( inList.first().lChar ).category() == QChar::Separator_Space) )
595 				inList.takeFirst();
596 		}
597 		if(inList.isEmpty())
598 			continue;
599 		if ( QChar ( inList.last().lChar ).category() == QChar::Separator_Space )
600 		{
601 			while ((!inList.isEmpty()) && ( QChar ( inList.last().lChar ).category() == QChar::Separator_Space ) )
602 				inList.takeLast();
603 		}
604 
605 
606 		QString dStr;
607 		QString dBk;
608 		foreach ( RenderedGlyph rg, inList )
609 		{
610 			dStr += QChar ( rg.lChar );
611 			dBk += rg.isBreak ? "#" : "_";
612 		}
613 		// 		qDebug() << "S"<<dStr;
614 		// 		qDebug() << "H"<<dBk;
615 
616 		// 		qDebug()<<"S E Sib Eib"<<start<<end<<theString.at(start).isBreak<<theString.at(end).isBreak;
617 
618 		GlyphList lg;
619 		/// * *
620 		if ( !hasHyph && !inList.last().isBreak )
621 		{
622 			// 			qDebug() <<"/// * *";
623 			lg = inList;
624 		}
625 		/// = *
626 		else if ( hasHyph && !inList.last().isBreak )
627 		{
628 			// 			qDebug() <<"/// = *";
629 
630 			GlyphList hr ( curHyph );
631 			hasHyph = false;
632 
633 			for ( int ih ( 0 ); ih < hr.count(); ++ih )
634 			{
635 				lg << hr[ih];
636 			}
637 
638 			while ( !inList.isEmpty() && QChar ( inList.first() .lChar ).category() != QChar::Separator_Space )
639 			{
640 				inList.takeFirst();
641 			}
642 
643 			if(!inList.isEmpty())
644 			{
645 				for ( int i ( 0 ); i < inList.count() ;++i )
646 					lg <<  inList.at ( i );
647 			}
648 
649 
650 		}
651 		/// * =
652 		else if ( !hasHyph && inList.last().isBreak )
653 		{
654 			// 			qDebug() <<"/// * =";
655 			GlyphList hr ( inList.last().hyphen.first );
656 			hasHyph = true;
657 			curHyph = inList.last().hyphen.second;
658 
659 			do
660 			{
661 				inList.takeLast();
662 			}
663 			while ( !inList.isEmpty() && QChar ( inList.last().lChar ).category() != QChar::Separator_Space  );
664 
665 
666 			if(!inList.isEmpty())
667 			{
668 				for ( int i ( 0 ); i < inList.count() ;++i )
669 					lg <<  inList.at ( i );
670 			}
671 
672 			QString dgS;
673 			for ( int ih ( 0 ); ih < hr.count(); ++ih )
674 			{
675 				lg <<  hr[ih];
676 				dgS +="("+ QString ( QChar ( lg.last().lChar ) ) +")";
677 			}
678 			QString dbh;for ( int h ( 0 );h<curHyph.count();++h ) {dbh+="["+ QString ( QChar ( curHyph[h].lChar ) ) +"]";}
679 			// 			qDebug() <<dgS<<"="<<dbh;
680 
681 		}
682 		/// = =
683 		else if ( hasHyph && inList.last().isBreak )
684 		{
685 
686 			// 			qDebug() <<"/// = =";
687 			GlyphList hr ( curHyph );
688 			GlyphList hr2 ( inList.last().hyphen.first );
689 
690 			hasHyph = true;
691 			curHyph = inList.last().hyphen.second;
692 
693 			for ( int ih ( 0 ); ih < hr.count(); ++ih )
694 			{
695 				lg <<  hr[ih];
696 			}
697 
698 			while ( !inList.isEmpty() && QChar ( inList.first() .lChar ).category() != QChar::Separator_Space )
699 			{
700 				inList.takeFirst();
701 			}
702 
703 			if(!inList.isEmpty())
704 			{
705 				do
706 				{
707 					inList.takeLast();
708 				}
709 				while ( !inList.isEmpty() &&  QChar ( inList.last().lChar ).category() != QChar::Separator_Space );
710 			}
711 
712 			QString dgS;
713 			if(!inList.isEmpty())
714 			{
715 				for ( int i ( 0 ); i < inList.count() ;++i )
716 					lg <<  inList.at ( i );
717 			}
718 
719 			for ( int ih ( 0 ); ih < hr2.count(); ++ih )
720 			{
721 				lg <<  hr2[ih];
722 				dgS +="("+ QString ( QChar ( lg.last().lChar ) ) +")";
723 			}
724 
725 			QString dbh;for ( int h ( 0 );h<curHyph.count();++h ) {dbh+="["+ QString ( QChar ( curHyph[h].lChar ) ) +"]";}
726 			// 			qDebug() <<dgS<<"="<<dbh;
727 
728 		}
729 
730 		lines << lg;
731 	}
732 
733 	TextProgression *tp = TextProgression::getInstance();
734 	bool verticalLayout ( tp->inBlock() == TextProgression::INLINE_BTT || tp->inBlock() == TextProgression::INLINE_TTB );
735 
736 	// 	qDebug()<<"lines finished";
737 	for ( int lI ( startLine ); lI<lines.count(); ++lI )
738 	{
739 		if ( stopIt )
740 			break;
741 
742 		GlyphList& lg ( lines[lI] );
743 
744 		if ( QChar ( lg.first().lChar ).category() == QChar::Separator_Space )
745 		{
746 			while ( QChar ( lg.first().lChar ).category() == QChar::Separator_Space  && !lg.isEmpty() )
747 				lg.takeFirst();
748 		}
749 		if ( QChar ( lg.last().lChar ).category() == QChar::Separator_Space )
750 		{
751 			while ( QChar ( lg.last().lChar ).category() == QChar::Separator_Space  && !lg.isEmpty() )
752 				lg.takeLast();
753 		}
754 
755 		clearCaches();
756 		double refW ( lineWidth( lI ) );
757 		double actualW( distance ( 0, lg.count(), lg ) );
758 		double diff ( refW - actualW );
759 		if(!deviceIndy)
760 		{
761 			// 			qDebug()<< "R1 A1"<<refW<<actualW;
762 			refW =  refW * 72.0  / typotek::getInstance()->getDpiX() ;
763 			actualW = actualW * 72.0 / typotek::getInstance()->getDpiX();
764 			// 			qDebug()<< "R2 A2"<<refW<<actualW;
765 			diff = refW - actualW ;
766 
767 		}
768 
769 		if ( lI != lines.count() - 1 || actualW > refW ) // not last line or last line is too long
770 		{
771 			QList<int> wsIds;
772 			// 			qDebug()<< "Ref Dis"<< refW << distance ( 0, lg.count(), lg );
773 			for ( int ri ( 0 ); ri < lg.count() ; ++ri )
774 			{
775 				if ( lg.at ( ri ).glyph &&  QChar ( lg.at ( ri ).lChar ).category() == QChar::Separator_Space )
776 				{
777 					wsIds << ri;
778 				}
779 			}
780 			double shareLost ( diff / qMax ( 1.0 , ( double ) wsIds.count() ) );
781 			//  			if(!oldIndy)
782 			// 				shareLost *= typotek::getInstance()->getDpiX() / 72.0;
783 			// 			qDebug() << "D N W"<<diff<<wsIds.count() << shareLost ;
784 			// 			qDebug()<<"R D F"<< refW << actualW << actualW + ((double)wsIds.count() * shareLost);
785 			if ( verticalLayout )
786 			{
787 				for ( int wi ( 0 ); wi < wsIds.count(); ++wi )
788 				{
789 					lg[ wsIds[wi] ].yadvance += shareLost;
790 				}
791 			}
792 			else
793 			{
794 				for ( int wi ( 0 ); wi < wsIds.count(); ++wi )
795 				{
796 					lg[ wsIds[wi] ].xadvance += shareLost;
797 				}
798 			}
799 		}
800 	}
801 	// 	qDebug() <<"doneLines:"<< lines.count() ;
802 
803 }
804 
doDraw()805 void FMLayout::doDraw()
806 {
807 	// Ask paths or pixmaps to theFont for each glyph and draw it on theScene
808 	// 	qDebug() <<"FMLayout::doDraw()";
809 	resetScene();
810 	QTime t;
811 	t.start();
812 	drawnLines = 0;
813 	TextProgression *tp = TextProgression::getInstance();
814 	QPointF pen ( origine );
815 	if( tp->inLine() != TextProgression::INLINE_BTT )
816 		pen.ry() += adjustedSampleInter;
817 	if( tp->inBlock() == TextProgression::BLOCK_RTL )
818 		pen.rx() -= adjustedSampleInter;
819 
820 	double pageTop(theRect.top());
821 	double pageRight(theRect.right());
822 	double pageBottom ( theRect.bottom() );
823 	double pageLeft(theRect.left());
824 
825 	double scale = fontSize / theFont->getUnitPerEm();
826 	double pixelAdjustX = typotek::getInstance()->getDpiX() / 72.0 ;
827 	double pixelAdjustY = typotek::getInstance()->getDpiY() / 72.0 ;
828 
829 	int pd =0;
830 
831 	for ( int lIdx ( 0 ); lIdx < lines.count() ; ++lIdx )
832 	{
833 		if ( stopIt )
834 			break;
835 		if ( tp->inLine() != TextProgression::INLINE_BTT &&  pen.y() > pageBottom )
836 			break;
837 		else if(tp->inLine() == TextProgression::INLINE_BTT || tp->inLine() == TextProgression::INLINE_TTB)
838 		{
839 			if(tp->inBlock() == TextProgression::BLOCK_RTL && pen.x() < pageLeft)
840 				break;
841 			else if(tp->inBlock() == TextProgression::BLOCK_LTR && pen.x() > pageRight)
842 				break;
843 		}
844 		++drawnLines;
845 		clearCaches();
846 		GlyphList refGlyph ( lines[lIdx] );
847 //		emit drawBaselineForMe(pen.y());
848 		if ( !deviceIndy )
849 		{
850 			for ( int i=0; i < refGlyph.count(); ++i )
851 			{
852 				if ( !refGlyph[i].glyph )
853 					continue;
854 				QGraphicsItem *glyph;
855 				if(contextIsMainThread)
856 					glyph =  theFont->itemFromGindexPix ( refGlyph[i].glyph , fontSize );
857 				else
858 					glyph = theFont->itemFromGindexPix_mt( refGlyph[i].glyph , fontSize );
859 				if ( !glyph )
860 					continue;
861 				if ( tp->inLine() == TextProgression::INLINE_RTL )
862 				{
863 					pen.rx() -= refGlyph[i].xadvance * pixelAdjustX ;
864 				}
865 				else if ( tp->inLine() == TextProgression::INLINE_BTT )
866 				{
867 					pen.ry() -= refGlyph[i].yadvance * pixelAdjustY;
868 				}
869 
870 				/*************************************************/
871 				if(contextIsMainThread)
872 				{
873 					pixList << reinterpret_cast<QGraphicsPixmapItem*>(glyph);
874 					theScene->addItem ( glyph );
875 					glyph->setZValue ( 100.0 );
876 					glyph->setPos ( pen.x()
877 							+ ( refGlyph[i].xoffset * pixelAdjustX )
878 							+ glyph->data (GLYPH_DATA_BITMAPLEFT).toDouble() * scale  ,
879 							pen.y()
880 							+ ( refGlyph[i].yoffset * pixelAdjustY )
881 							- glyph->data(GLYPH_DATA_BITMAPTOP).toDouble());
882 				}
883 				else
884 				{
885 //					refGlyph[i].dump();
886 					MetaGlyphItem * mgi(reinterpret_cast<MetaGlyphItem*>(glyph));
887 //					qDebug()<<refGlyph[i].glyph<<pen.y() << ( refGlyph[i].yoffset * pixelAdjustY ) << mgi->metaData ( GLYPH_DATA_BITMAPTOP ).toDouble();
888 					++pd;
889 					emit drawPixmapForMe(refGlyph[i].glyph,
890 							     fontSize,
891 							     pen.x()
892 							     + (refGlyph[i].xoffset * pixelAdjustX)
893 							     + mgi->metaData(GLYPH_DATA_BITMAPLEFT).toDouble() * scale,
894 							     pen.y()
895 							     + (refGlyph[i].yoffset * pixelAdjustY)
896 							     - mgi->metaData(GLYPH_DATA_BITMAPTOP).toDouble());
897 				}
898 				/*************************************************/
899 
900 				if ( tp->inLine() == TextProgression::INLINE_LTR )
901 					pen.rx() += refGlyph[i].xadvance * pixelAdjustX ;
902 				else if ( tp->inLine() == TextProgression::INLINE_TTB )
903 					pen.ry() += refGlyph[i].yadvance * pixelAdjustY ;
904 			}
905 		}
906 		else
907 		{
908 			for ( int i=0; i < refGlyph.count(); ++i )
909 			{
910 				if ( !refGlyph[i].glyph )
911 					continue;
912 				QGraphicsPathItem *glyph = theFont->itemFromGindex ( refGlyph[i].glyph , fontSize );
913 
914 				if ( tp->inLine() == TextProgression::INLINE_RTL )
915 				{
916 					pen.rx() -= refGlyph[i].xadvance ;
917 				}
918 				else if ( tp->inLine() == TextProgression::INLINE_BTT )
919 				{
920 					pen.ry() -= refGlyph[i].yadvance ;
921 				}
922 				/**********************************************/
923 				glyphList << glyph;
924 				theScene->addItem ( glyph );
925 				glyph->setPen(Qt::NoPen);
926 #ifdef BUILD_TYPE_DEBUG
927 				// visual debug
928 				// 				QColor dbgColor( i * 255 / refGlyph.count() , i * 255 / refGlyph.count() , 0, 125);
929 				// 				glyph->setBrush(dbgColor);
930 				if(refGlyph[i].lChar == 32) glyph->setBrush(Qt::blue);
931 				//end visual debug
932 #endif
933 				glyph->setPos ( pen.x() + ( refGlyph[i].xoffset ),
934 				                pen.y() + ( refGlyph[i].yoffset ) );
935 				glyph->setZValue ( 100.0 );
936 				/*******************************************/
937 
938 				if ( tp->inLine() == TextProgression::INLINE_LTR )
939 					pen.rx() += refGlyph[i].xadvance ;
940 				else if ( tp->inLine() == TextProgression::INLINE_TTB )
941 					pen.ry() += refGlyph[i].yadvance;
942 
943 			}
944 		}
945 
946 		if ( tp->inBlock() == TextProgression::BLOCK_TTB )
947 		{
948 			pen.ry() += adjustedSampleInter;
949 			pen.rx() = origine.x() ;
950 		}
951 		else if ( tp->inBlock() == TextProgression::BLOCK_RTL )
952 		{
953 			if(tp->inLine() == TextProgression::INLINE_TTB)
954 				pen.ry() = origine.y() + adjustedSampleInter;
955 			else
956 				pen.ry() = origine.y();
957 			pen.rx() -= adjustedSampleInter;
958 		}
959 		else if ( tp->inBlock() == TextProgression::BLOCK_LTR )
960 		{
961 			if(tp->inLine() == TextProgression::INLINE_TTB)
962 				pen.ry() = origine.y() + adjustedSampleInter;
963 			else
964 				pen.ry() = origine.y();
965 			pen.rx() += adjustedSampleInter;
966 		}
967 
968 		//		typotek::getInstance()->runProgressJob();
969 		// 		qDebug() <<"P"<<pen;
970 	}
971 	//
972 	// 	qDebug() <<"doDraw T(ms)"<<t.elapsed();
973 	emit paintFinished();
974 	emit drawPixmapForMe(-1,0,0,0);
975 	qDebug()<<"P emitted:"<<pd;
976 }
977 
sepCount(int start,int end,const GlyphList & gl)978 int FMLayout::sepCount(int start, int end, const GlyphList & gl)
979 {
980 	if ( sepCache.contains ( start ) )
981 	{
982 		if ( sepCache[start].contains ( end ) )
983 			return sepCache[start][end];
984 	}
985 	int storeStart(start);
986 	int storeEnd(end);
987 
988 	GlyphList gList = gl;
989 	if ( QChar ( gList.first().lChar ).category() == QChar::Separator_Space )
990 	{
991 		while ( QChar ( gList.first().lChar ).category() == QChar::Separator_Space && start < end )
992 		{
993 			++start;
994 		}
995 	}
996 	if ( QChar ( gList.last().lChar ).category() == QChar::Separator_Space )
997 	{
998 		while ( QChar ( gList.last().lChar ).category() == QChar::Separator_Space && end > start )
999 		{
1000 			--end;
1001 		}
1002 	}
1003 	int ret(0);
1004 	for ( int i ( start ); i < end ;++i )
1005 	{
1006 		if(QChar ( gList.at( i ).lChar  ).category() == QChar::Separator_Space )
1007 			++ret;
1008 	}
1009 	sepCache[storeStart][storeEnd] = ret;
1010 	return ret;
1011 }
1012 
1013 // please move this method to GlyphList itself
distance(int start,int end,const GlyphList & gl,bool strip)1014 double FMLayout::distance ( int start, int end, const GlyphList& gl, bool strip )
1015 {
1016 	// 	qDebug()<<"distance(start ="<<start<<",end"<<end<<",strip"<<strip<<" )";
1017 	if(!strip)
1018 	{
1019 		if ( distCache.contains ( start ) )
1020 		{
1021 			if ( distCache[start].contains ( end ) )
1022 				return distCache[start][end];
1023 		}
1024 	}
1025 	else
1026 	{
1027 		if ( stripCache.contains ( start ) )
1028 		{
1029 			if ( stripCache[start].contains ( end ) )
1030 				return stripCache[start][end];
1031 		}
1032 	}
1033 	int storeStart(start);
1034 	int storeEnd(end);
1035 	bool hasHyph(false);
1036 	GlyphList hyphList;
1037 	if(start>0 && gl[start - 1].isBreak)
1038 	{
1039 		hasHyph = true;
1040 		hyphList = gl[start - 1].hyphen.second;
1041 	}
1042 	GlyphList gList = gl;
1043 	// 	if ( QChar ( gList.first().lChar ).category() == QChar::Separator_Space )
1044 	// 	{
1045 	// 		while ( QChar ( gList.first().lChar ).category() == QChar::Separator_Space && start < end )
1046 	// 		{
1047 	// 			++start;
1048 	// 		}
1049 	// 	}
1050 	// 	if ( QChar ( gList.last().lChar ).category() == QChar::Separator_Space )
1051 	// 	{
1052 	// 		while ( QChar ( gList.last().lChar ).category() == QChar::Separator_Space && end > start )
1053 	// 		{
1054 	// 			--end;
1055 	// 		}
1056 	// 	}
1057 	//
1058 	TextProgression *tp = TextProgression::getInstance();
1059 	bool verticalLayout ( tp->inLine() == TextProgression::INLINE_BTT || tp->inLine() == TextProgression::INLINE_TTB );
1060 	// 	qDebug()<<"IB VL"<<tp->inLine()<<verticalLayout;
1061 	// 	QString dStr;
1062 	// 	for(int di(start); di < end; ++di )
1063 	// 	{
1064 	// 		dStr += QChar(gl[di].lChar);
1065 	// 	}
1066 	// 	qDebug()<< "SD"<<dStr;
1067 
1068 	// 	qDebug()<<"DIS C S E"<<gList.count()<< start<< end;
1069 	double ret ( 0.0 );
1070 	if ( end <= start )
1071 	{
1072 		// 		qDebug()<<"ERR_LOGIC! S E"<< start<< end;
1073 		return ret;
1074 	}
1075 	int EXend ( end -1 );
1076 	if ( verticalLayout )
1077 	{
1078 		if ( !hasHyph && !gList.at ( EXend ).isBreak )
1079 		{
1080 			for ( int i ( start ); i < end ;++i )
1081 			{
1082 				// 				qDebug()<<"Ya"<< gList.at ( i ).yadvance;
1083 				if(strip)
1084 				{
1085 					if( QChar ( gList.last().lChar ).category() != QChar::Separator_Space)
1086 						ret += gList.at ( i ).yadvance;
1087 				}
1088 				else
1089 					ret += gList.at ( i ).yadvance;
1090 			}
1091 		}
1092 		else if ( hasHyph && !gList.at ( EXend ).isBreak )
1093 		{
1094 			GlyphList hr ( gList.at ( start - 1).hyphen.second );
1095 			for ( int ih ( 0 ); ih < hr.count(); ++ih )
1096 			{
1097 				ret += hr[ih].yadvance;
1098 			}
1099 			int bp ( start );
1100 			while ( bp < end )
1101 			{
1102 				if ( QChar ( gList.at ( bp ).lChar ).category() != QChar::Separator_Space )
1103 					++bp ;
1104 				else
1105 					break;
1106 			}
1107 
1108 			for ( int i ( bp ); i < end ;++i )
1109 			{
1110 				if(strip)
1111 				{
1112 					if( QChar ( gList.last().lChar ).category() != QChar::Separator_Space)
1113 						ret += gList.at ( i ).yadvance;
1114 				}
1115 				else
1116 					ret += gList.at ( i ).yadvance ;
1117 			}
1118 		}
1119 		else if ( !hasHyph && gList.at ( EXend ).isBreak )
1120 		{
1121 			int bp ( end );
1122 			while ( QChar ( gList.at ( bp ).lChar ).category() != QChar::Separator_Space &&  bp > start ) --bp ;
1123 			++bp;
1124 
1125 			for ( int i ( start ); i < bp ;++i )
1126 			{
1127 				if(strip)
1128 				{
1129 					if( QChar ( gList.last().lChar ).category() != QChar::Separator_Space)
1130 						ret += gList.at ( i ).yadvance;
1131 				}
1132 				else
1133 					ret += gList.at ( i ).yadvance;
1134 			}
1135 			GlyphList hr ( gList.at ( EXend ).hyphen.first );
1136 			for ( int ih ( 0 ); ih < hr.count(); ++ih )
1137 			{
1138 				ret += hr[ih].yadvance;
1139 			}
1140 
1141 		}
1142 		else if ( hasHyph && gList.at ( EXend ).isBreak )
1143 		{
1144 			GlyphList hr ( hyphList );
1145 			for ( int ih ( 0 ); ih < hr.count(); ++ih )
1146 			{
1147 				ret += hr[ih].yadvance;
1148 			}
1149 			int bpS ( start );
1150 			while ( bpS < end )
1151 			{
1152 				if ( QChar ( gList.at ( bpS ).lChar ).category() != QChar::Separator_Space )
1153 					++bpS ;
1154 				else
1155 					break;
1156 			}
1157 
1158 			int bpE ( end );
1159 			while ( QChar ( gList.at ( bpE ).lChar ).category() != QChar::Separator_Space &&  bpE > start ) --bpE ;
1160 			++bpE;
1161 
1162 			for ( int i ( bpS ); i < bpE ;++i )
1163 			{
1164 				if(strip)
1165 				{
1166 					if( QChar ( gList.last().lChar ).category() != QChar::Separator_Space)
1167 						ret += gList.at ( i ).yadvance;
1168 				}
1169 				else
1170 					ret += gList.at ( i ).yadvance;
1171 			}
1172 			GlyphList hr2 ( gList.at ( EXend ).hyphen.first );
1173 			for ( int ih ( 0 ); ih < hr2.count(); ++ih )
1174 			{
1175 				ret += hr2[ih].yadvance;
1176 			}
1177 		}
1178 	}
1179 	else
1180 	{
1181 		if ( !hasHyph && !gList.at ( EXend ).isBreak )
1182 		{
1183 			// 					qDebug()<<". ." ;
1184 			for ( int i ( start ); i < end ;++i )
1185 			{
1186 				if(strip)
1187 				{
1188 					if( QChar ( gList.at(i).lChar ).category() != QChar::Separator_Space)
1189 						ret += gList.at ( i ).xadvance;
1190 				}
1191 				else
1192 					ret += gList.at ( i ).xadvance /*+ gList.at ( i ).xoffset*/;
1193 			}
1194 		}
1195 		else if ( hasHyph && !gList.at ( EXend ).isBreak )
1196 		{
1197 			// 					qDebug()<<"- ." ;
1198 			GlyphList hr ( hyphList );
1199 			for ( int ih ( 0 ); ih < hr.count(); ++ih )
1200 			{
1201 				// 			qDebug()<<"ih"<<ih;
1202 				ret += hr[ih].xadvance;
1203 			}
1204 			int bp ( start );
1205 			// 		qDebug()<<"bp"<<bp;
1206 			while ( bp < end )
1207 			{
1208 				if ( QChar ( gList.at ( bp ).lChar ).category() != QChar::Separator_Space )
1209 					++bp ;
1210 				else
1211 					break;
1212 			}
1213 
1214 			for ( int i ( bp ); i < end ;++i )
1215 			{
1216 				// 			qDebug()<<"i tS.xa"<<i<<gList.at ( i ).xadvance;
1217 				if(strip)
1218 				{
1219 					if( QChar ( gList.at(i).lChar ).category() != QChar::Separator_Space)
1220 						ret += gList.at ( i ).xadvance;
1221 				}
1222 				else
1223 					ret += gList.at ( i ).xadvance ;
1224 			}
1225 
1226 
1227 		}
1228 		else if ( !hasHyph && gList.at ( EXend ).isBreak )
1229 		{
1230 			// 					qDebug()<<". -" ;
1231 			int bp ( end );
1232 			while ( QChar ( gList.at ( bp ).lChar ).category() != QChar::Separator_Space &&  bp > start ) --bp ;
1233 			++bp;
1234 
1235 			for ( int i ( start ); i < bp ;++i )
1236 			{
1237 				if(strip)
1238 				{
1239 					if( QChar ( gList.at(i).lChar ).category() != QChar::Separator_Space)
1240 						ret += gList.at ( i ).xadvance;
1241 				}
1242 				else
1243 					ret += gList.at ( i ).xadvance /*+ gList.at ( i ).xoffset*/;
1244 			}
1245 			GlyphList hr ( gList.at ( EXend ).hyphen.first );
1246 			for ( int ih ( 0 ); ih < hr.count(); ++ih )
1247 			{
1248 				ret += hr[ih].xadvance;
1249 			}
1250 
1251 		}
1252 		else if ( hasHyph && gList.at ( EXend ).isBreak )
1253 		{
1254 			// 					qDebug()<<"- -" ;
1255 			// 			QString debStr;
1256 			GlyphList hr ( hyphList);
1257 			for ( int ih ( 0 ); ih < hr.count(); ++ih )
1258 			{
1259 				// 				debStr += QChar(hr[ih].lChar);
1260 				ret += hr[ih].xadvance;
1261 			}
1262 
1263 			int bpS ( start );
1264 			while ( bpS < end )
1265 			{
1266 				if ( QChar ( gList.at ( bpS ).lChar ).category() != QChar::Separator_Space )
1267 					++bpS ;
1268 				else
1269 					break;
1270 			}
1271 
1272 			int bpE ( end );
1273 			while ( QChar ( gList.at ( bpE ).lChar ).category() != QChar::Separator_Space &&  bpE > start ) --bpE ;
1274 			++bpE;
1275 
1276 			for ( int i ( bpS ); i < bpE ;++i )
1277 			{
1278 				if(strip)
1279 				{
1280 					if( QChar ( gList.at(i).lChar ).category() != QChar::Separator_Space)
1281 						ret += gList.at ( i ).xadvance;
1282 				}
1283 				else
1284 				{
1285 					// 					debStr += QChar(gList.at( i ).lChar);
1286 					ret += gList.at ( i ).xadvance /*+ gList.at ( i ).xoffset*/;
1287 				}
1288 			}
1289 			GlyphList hr2 ( gList.at ( EXend ).hyphen.first );
1290 			for ( int ih ( 0 ); ih < hr2.count(); ++ih )
1291 			{
1292 				// 				debStr += QChar(hr2[ih].lChar);
1293 				ret += hr2[ih].xadvance;
1294 			}
1295 			// 			qDebug()<<"D(= =)"<<debStr;
1296 		}
1297 	}
1298 
1299 	// 	qDebug()<<"SID" ;
1300 	if(!deviceIndy)
1301 		ret *= typotek::getInstance()->getDpiX() / 72.0 ;
1302 	// 	if ( power )
1303 	// 	{
1304 	// 		distCache[storeStart][storeEnd] = ret*ret;
1305 	// 		return ret*ret;
1306 	// 	}
1307 	distCache[start][end] = ret;
1308 	return ret;
1309 }
1310 
resetScene()1311 void FMLayout::resetScene()
1312 {
1313 	if(persistentScene)
1314 		return;
1315 	if(!contextIsMainThread)
1316 	{
1317 		emit clearScene();
1318 		return;
1319 	}
1320 	int pCount ( pixList.count() );
1321 	for ( int i = 0; i < pCount ; ++i )
1322 	{
1323 		if ( pixList[i]->scene() && ( pixList[i]->scene() == theScene ) )
1324 		{
1325 			pixList[i]->scene()->removeItem ( pixList[i] );
1326 			delete pixList[i];
1327 			pixList[i] = 0;
1328 		}
1329 	}
1330 	pixList.removeAll(0);
1331 
1332 	int gCount ( glyphList.count() );
1333 	// 	QMap<QGraphicsScene*,int> ss;
1334 	for ( int i = 0; i < gCount; ++i )
1335 	{
1336 		// 		ss[glyphList[i]->scene()]++;
1337 		if ( glyphList[i]->scene() && (glyphList[i]->scene() == theScene) )
1338 		{
1339 			glyphList[i]->scene()->removeItem ( glyphList[i] );
1340 			delete glyphList[i];
1341 			glyphList[i] = 0;
1342 		}
1343 	}
1344 	glyphList.removeAll(0);
1345 	// 	QString dbs;
1346 	// 	foreach(QGraphicsScene* qgs, ss)
1347 	// 	{
1348 	// 		dbs += "["+ QString::number(reinterpret_cast<unsigned int>(qgs)) +"]";
1349 	// 	}
1350 	// 	qDebug()<<"GC"<< ss <<gCount<< r <<glyphList.count() ;
1351 }
1352 
1353 //void FMLayout::setTheScene ( QGraphicsScene* theValue , QRectF rect)
1354 //{
1355 //	theScene = theValue;
1356 //	if(rect.isNull())
1357 //	{
1358 //		QRectF tmpRect = theScene->sceneRect();
1359 //		double sUnitW ( tmpRect.width() * .1 );
1360 //		double sUnitH ( tmpRect.height() * .1 );
1361 
1362 //		theRect.setX ( 2.0 * sUnitW );
1363 //		theRect.setY ( 1.0 * sUnitH );
1364 //		theRect.setWidth ( 6.0 * sUnitW );
1365 //		theRect.setHeight ( 8.0 * sUnitH );
1366 //	}
1367 //	else
1368 //	{
1369 //		theRect = rect;
1370 //	}
1371 //// 	rules->setRect(theRect);
1372 //// 	rules->setZValue(9.9);
1373 //// 	if(rules->scene() != theScene)
1374 //// 		theScene->addItem(rules);
1375 //}
1376 
1377 //void FMLayout::setTheFont ( FontItem* theValue )
1378 //{
1379 //	theFont = theValue;
1380 //	optionHasChanged = true;
1381 //}
1382 
lineWidth(int l)1383 double FMLayout::lineWidth ( int l )
1384 {
1385 	TextProgression *tp = TextProgression::getInstance();
1386 	double offset ( ( double ) l * adjustedSampleInter ) ;
1387 	if ( tp->inBlock() == TextProgression::BLOCK_TTB )
1388 	{
1389 		// 		if ( theRect.top() + offset >  theRect.bottom() )
1390 		// 		{
1391 		// 			return OUT_OF_RECT;
1392 		// 		}
1393 		return theRect.width();
1394 	}
1395 	else if ( tp->inBlock() == TextProgression::BLOCK_RTL )
1396 	{
1397 		// 		if ( theRect.right() - offset <  theRect.left() )
1398 		// 		{
1399 		// 			return OUT_OF_RECT;
1400 		// 		}
1401 		return theRect.height() - adjustedSampleInter;
1402 	}
1403 	else if ( tp->inBlock() == TextProgression::BLOCK_LTR )
1404 	{
1405 		// 		if ( theRect.left() + offset >  theRect.right() )
1406 		// 		{
1407 		// 			return OUT_OF_RECT;
1408 		// 		}
1409 		return theRect.height() - adjustedSampleInter;
1410 	}
1411 
1412 	return OUT_OF_RECT;
1413 }
1414 
slotOption(int v)1415 void FMLayout::slotOption ( int v )
1416 {
1417 	optionHasChanged = true;
1418 
1419 	if ( v == FMLayOptWidget::BEFORE )
1420 	{
1421 		FM_LAYOUT_NODE_SOON_F = optionsWidget->getValue ( FMLayOptWidget::BEFORE );
1422 		emit updateLayout();
1423 	}
1424 	else if ( v == FMLayOptWidget::EXACT )
1425 	{
1426 		FM_LAYOUT_NODE_FIT_F = optionsWidget->getValue ( FMLayOptWidget::EXACT );
1427 		emit updateLayout();
1428 	}
1429 	else if ( v == FMLayOptWidget::AFTER )
1430 	{
1431 		FM_LAYOUT_NODE_LATE_F = optionsWidget->getValue ( FMLayOptWidget::AFTER );
1432 		emit updateLayout();
1433 	}
1434 	else if ( v == FMLayOptWidget::END )
1435 	{
1436 		FM_LAYOUT_NODE_END_F = optionsWidget->getValue ( FMLayOptWidget::END );
1437 		emit updateLayout();
1438 	}
1439 	else if ( v == FMLayOptWidget::HYPHEN )
1440 	{
1441 		FM_LAYOUT_HYPHEN_PENALTY = ( double ) optionsWidget->getValue ( FMLayOptWidget::HYPHEN ) / 10.0;
1442 		emit updateLayout();
1443 	}
1444 	else if ( v == FMLayOptWidget::SPACE )
1445 	{
1446 		FM_LAYOUT_MAX_COMPRESSION = optionsWidget->getValue ( FMLayOptWidget::SPACE );
1447 		emit updateLayout();
1448 	}
1449 }
1450 
setAdjustedSampleInter(double theValue)1451 void FMLayout::setAdjustedSampleInter(double theValue)
1452 {
1453 	adjustedSampleInter = !deviceIndy ? theValue * typotek::getInstance()->getDpiX() / 72.0 : theValue;
1454 }
1455 
clearCaches()1456 void FMLayout::clearCaches()
1457 {
1458 	sepCache.clear();
1459 	distCache.clear();
1460 	stripCache.clear();
1461 }
1462 
endOfParagraph()1463 void FMLayout::endOfParagraph()
1464 {
1465 	//	typotek::getInstance()->runProgressJob();
1466 }
1467 
1468 
setContext(bool c)1469 void FMLayout::setContext(bool c)
1470 {
1471 	contextIsMainThread = c;
1472 //	if(c)
1473 //		theFont->moveToThread(QApplication::instance()->thread());
1474 //	else
1475 //		emit objectWanted(theFont);
1476 }
1477 
1478 
1479 
1480 
1481 
1482