Rev 21218 |
Rev 21240 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/
/***************************************************************************
pageitem.cpp - description
-------------------
begin : Sat Apr 7 2001
copyright : (C) 2001 by Franz Schmid
email : Franz.Schmid@altmuehlnet.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "pageitem_textframe.h"
#include <QDebug>
#include <QList>
#include <QTransform>
#include <QPalette>
#include <QPoint>
#include <QPolygon>
#include <QRegion>
#include <cairo.h>
#include <cassert>
#include "actionmanager.h"
#include "appmodes.h"
#include "canvas.h"
#include "commonstrings.h"
#include "hyphenator.h"
#include "marks.h"
#include "notesstyles.h"
#include "numeration.h"
#include "pageitem.h"
#include "pageitem_group.h"
#include "pageitem_noteframe.h"
#include "prefsmanager.h"
#include "scconfig.h"
#include "scpage.h"
#include "scpainter.h"
#include "scpaths.h"
#include "scraction.h"
#include "scribus.h"
#include "scribusdoc.h"
#include "scribusview.h"
#include "scribusstructs.h"
#include "selection.h"
#include "text/boxes.h"
#include "text/screenpainter.h"
#include "text/textshaper.h"
#include "ui/guidemanager.h"
#include "ui/marksmanager.h"
#include "undomanager.h"
#include "undostate.h"
#include "units.h"
#include "util.h"
#include "util_math.h"
using namespace std;
PageItem_TextFrame::PageItem_TextFrame(ScribusDoc *pa, double x, double y, double w, double h, double w2, QString fill, QString outline)
: PageItem(pa, PageItem::TextFrame, x, y, w, h, w2, fill, outline)
{
invalid = true;
firstChar = 0;
cursorBiasBackward = false;
unicodeTextEditMode = false;
unicodeInputCount = 0;
unicodeInputString = "";
m_origAnnotPos = QRectF(xPos(), yPos(), width(), height());
verticalAlign = 0;
connect(&itemText,SIGNAL(changed()), this, SLOT(slotInvalidateLayout()));
}
PageItem_TextFrame::PageItem_TextFrame(const PageItem & p) : PageItem(p)
{
invalid = true;
cursorBiasBackward = false;
unicodeTextEditMode = false;
unicodeInputCount = 0;
unicodeInputString = "";
m_notesFramesMap.clear();
m_origAnnotPos = QRectF(xPos(), yPos(), width(), height());
verticalAlign = 0;
connect(&itemText,SIGNAL(changed()), this, SLOT(slotInvalidateLayout()));
}
static QRegion itemShape(PageItem* docItem, double xOffset, double yOffset)
{
QRegion res;
QTransform pp;
if (docItem->isGroupChild())
pp.translate(docItem->gXpos, docItem->gYpos);
else
pp.translate(docItem->xPos() - xOffset, docItem->yPos() - yOffset);
pp.rotate(docItem->rotation());
if (docItem->textFlowUsesBoundingBox())
{
QRectF bb = docItem->getVisualBoundingRect();
if (docItem->isGroupChild())
{
bb.translate(-docItem->xPos(), -docItem->yPos());
bb.translate(docItem->gXpos, docItem->gYpos);
}
res = QRegion(bb.toRect());
}
else if ((docItem->textFlowUsesImageClipping()) && (docItem->imageClip.size() != 0))
{
QList<uint> Segs;
QPolygon Clip2 = FlattenPath(docItem->imageClip, Segs);
res = QRegion(pp.map(Clip2)).intersected(QRegion(pp.map(docItem->Clip)));
}
else if ((docItem->textFlowUsesContourLine()) && (docItem->ContourLine.size() != 0))
{
QList<uint> Segs;
QPolygon Clip2 = FlattenPath(docItem->ContourLine, Segs);
res = QRegion(pp.map(Clip2));
}
else
{
if (docItem->isSymbol() || docItem->isGroup())
{
if (docItem->imageFlippedH())
{
pp.translate(docItem->width(), 0);
pp.scale(-1, 1);
}
if (docItem->imageFlippedV())
{
pp.translate(0, docItem->height());
pp.scale(1, -1);
}
}
if ((((docItem->lineColor() != CommonStrings::None) || (!docItem->patternStrokeVal.isEmpty()) || (docItem->GrTypeStroke > 0)) && (docItem->lineWidth() > 1)) || (!docItem->NamedLStyle.isEmpty()))
{
QVector<double> m_array;
QPainterPath ppa;
QPainterPath result;
if (docItem->itemType() == PageItem::PolyLine)
ppa = docItem->PoLine.toQPainterPath(false);
else
ppa = docItem->PoLine.toQPainterPath(true);
if (docItem->NamedLStyle.isEmpty())
{
QPainterPathStroker stroke;
stroke.setCapStyle(docItem->lineEnd());
stroke.setJoinStyle(docItem->lineJoin());
stroke.setDashPattern(Qt::SolidLine);
stroke.setWidth(docItem->lineWidth());
result = stroke.createStroke(ppa);
}
else
{
multiLine ml = docItem->doc()->MLineStyles[docItem->NamedLStyle];
int ind = ml.size()-1;
if ((ml[ind].Color != CommonStrings::None) && (ml[ind].Width != 0))
{
QPainterPathStroker stroke;
stroke.setCapStyle(static_cast<Qt::PenCapStyle>(ml[ind].LineEnd));
stroke.setJoinStyle(static_cast<Qt::PenJoinStyle>(ml[ind].LineJoin));
stroke.setDashPattern(Qt::SolidLine);
stroke.setWidth(ml[ind].Width);
result = stroke.createStroke(ppa);
}
}
res = QRegion(pp.map(docItem->Clip));
QList<QPolygonF> pl = result.toSubpathPolygons();
for (int b = 0; b < pl.count(); b++)
{
res = res.united(QRegion(pp.map(pl[b].toPolygon())));
}
}
else
res = QRegion(pp.map(docItem->Clip));
}
return res;
}
QRegion PageItem_TextFrame::calcAvailableRegion()
{
QRegion result(this->Clip);
if ((!isEmbedded) || (isGroupChild()))
{
bool invertible(false);
QTransform canvasToLocalMat;
if (isGroupChild())
canvasToLocalMat.translate(gXpos, gYpos);
else
canvasToLocalMat.translate(m_xPos, m_yPos);
canvasToLocalMat.rotate(m_rotation);
canvasToLocalMat = canvasToLocalMat.inverted(&invertible);
if (!invertible) return QRegion();
int LayerLev = m_Doc->layerLevelFromID(LayerID);
uint docItemsCount=m_Doc->Items->count();
ScPage* Mp=0;
ScPage* Dp=0;
PageItem* docItem=0;
int LayerLevItem;
QList<PageItem*> thisList;
if (!OnMasterPage.isEmpty())
{
if ((savedOwnPage == -1) || (savedOwnPage >= signed(m_Doc->Pages->count())))
return result;
Mp = m_Doc->MasterPages.at(m_Doc->MasterNames[OnMasterPage]);
Dp = m_Doc->Pages->at(savedOwnPage);
if (isGroupChild())
thisList = Parent->asGroupFrame()->groupItemList;
else
thisList = m_Doc->MasterItems;
int thisid = thisList.indexOf(this);
for (int a = 0; a < m_Doc->MasterItems.count(); ++a)
{
docItem = m_Doc->MasterItems.at(a);
// #10642 : masterpage items interact only with items placed on same masterpage
if (docItem->OnMasterPage != OnMasterPage)
continue;
int did = m_Doc->MasterItems.indexOf(docItem);
LayerLevItem = m_Doc->layerLevelFromID(docItem->LayerID);
if (((did > thisid) && (docItem->LayerID == LayerID)) || (LayerLevItem > LayerLev && m_Doc->layerFlow(docItem->LayerID)))
{
if (docItem->textFlowAroundObject())
{
QRegion itemRgn = itemShape(docItem, Mp->xOffset() - Dp->xOffset(), Mp->yOffset() - Dp->yOffset());
result = result.subtracted( canvasToLocalMat.map(itemRgn) );
}
}
} // for all masterItems
// (JG) #6009 : disable possible interaction between master text frames and normal frames
// which have the text flow option set
/*if (!m_Doc->masterPageMode())
{
for (uint a = 0; a < docItemsCount; ++a)
{
docItem = m_Doc->Items->at(a);
Mp = m_Doc->MasterPages.at(m_Doc->MasterNames[OnMasterPage]);
Dp = m_Doc->Pages->at(OwnPage);
if ((docItem->textFlowAroundObject()) && (docItem->OwnPage == OwnPage))
{
result = result.subtract(itemShape(docItem, m_Doc->view(), Mp->xOffset() - Dp->xOffset(), Mp->yOffset() - Dp->yOffset()));
}
} // for all docItems
} // if (! masterPageMode) */
} // if (!OnMasterPage.isEmpty())
else
{
int thisid = 0;
if (isGroupChild())
{
thisid = Parent->asGroupFrame()->groupItemList.indexOf(this);
docItemsCount = Parent->asGroupFrame()->groupItemList.count();
for (uint a = 0; a < docItemsCount; ++a)
{
docItem = Parent->asGroupFrame()->groupItemList.at(a);
int did = Parent->asGroupFrame()->groupItemList.indexOf(docItem);
if (did > thisid)
{
if (docItem->textFlowAroundObject())
{
QRegion itemRgn = itemShape(docItem, 0, 0);
result = result.subtracted( canvasToLocalMat.map(itemRgn) );
}
}
}
}
else
{
thisid = m_Doc->Items->indexOf(this);
for (uint a = 0; a < docItemsCount; ++a)
{
docItem = m_Doc->Items->at(a);
int did = m_Doc->Items->indexOf(docItem);
LayerLevItem = m_Doc->layerLevelFromID(docItem->LayerID);
if (((did > thisid) && (docItem->LayerID == LayerID)) || (LayerLevItem > LayerLev && m_Doc->layerFlow(docItem->LayerID)))
{
if (docItem->textFlowAroundObject())
{
QRegion itemRgn = itemShape(docItem, 0, 0);
result = result.subtracted( canvasToLocalMat.map(itemRgn) );
}
}
}
} // for all docItems
} // if(OnMasterPage.isEmpty()
} // if(!Embedded)
//else
// qDebug() << "QRegion empty";
return result;
}
void PageItem_TextFrame::setShadow()
{
if (OnMasterPage.isEmpty())
return;
QString newShadow = m_Doc->masterPageMode() ? OnMasterPage : QString::number(OwnPage);
if (newShadow != m_currentShadow) {
if (m_currentShadow == OnMasterPage) {
// masterpage was edited, clear all shadows
m_shadows.clear();
}
if (!m_shadows.contains(newShadow)) {
if (!m_shadows.contains(OnMasterPage)) {
m_shadows[OnMasterPage] = itemText;
// const ParagraphStyle& pstyle(shadows[OnMasterPage].paragraphStyle(0));
// qDebug() << QString("Pageitem_Textframe: style of master: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment());
// qDebug() << QString("Pageitem_Textframe: shadow itemText->%1").arg(OnMasterPage);
}
if (newShadow != OnMasterPage) {
m_shadows[newShadow] = m_shadows[OnMasterPage].copy();
// const ParagraphStyle& pstyle(shadows[newShadow].paragraphStyle(0));
// qDebug() << QString("Pageitem_Textframe: style of shadow copy: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment());
}
// qDebug() << QString("Pageitem_Textframe: shadow %1<-%2").arg(newShadow).arg(OnMasterPage);
}
itemText = m_shadows[newShadow];
// const ParagraphStyle& pstyle(itemText.paragraphStyle(0));
// qDebug() << QString("Pageitem_Textframe: style of shadow: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment());
invalid = true;
m_currentShadow = newShadow;
}
}
/*
static void debugLineLayout(const StoryText& itemText, const LineSpec& line)
{
QFile debugFile(QDir::homePath() + "/Desktop/debug_line.csv");
debugFile.open(QIODevice::WriteOnly);
QTextStream stream(&debugFile);
stream.setRealNumberNotation(QTextStream::FixedNotation);
stream.setRealNumberPrecision(7);
stream << "xoffset" << "\t";
stream << "yoffset" << "\t";
stream << "xadvance" << "\t";
stream << "yadvance" << "\t";
stream << "scaleH" << "\t";
stream << "scaleV" << "\t";
stream << "\n";
for (int zc = line.firstChar; zc < line.lastChar; ++zc)
{
const ScText* item = itemText.item(zc);
stream << item->glyph.xoffset << "\t";
stream << item->glyph.yoffset << "\t";
stream << item->glyph.xadvance << "\t";
stream << item->glyph.yadvance << "\t";
stream << item->glyph.scaleH << "\t";
stream << item->glyph.scaleV << "\t";
stream << "\n";
}
debugFile.close();
}
static void dumpIt(const ParagraphStyle& pstyle, QString indent = QString("->"))
{
QString db = QString("%6%1/%2 @ %3: %4--%5 linespa%6: %7 align%8")
.arg(pstyle.name())
.arg(pstyle.parent())
.arg( (unsigned long int) &pstyle)
.arg(pstyle.leftMargin())
.arg(pstyle.rightMargin())
.arg(indent)
.arg(pstyle.lineSpacingMode())
.arg(pstyle.lineSpacing())
.arg(pstyle.alignment());
qDebug() << db;
static QString more(" ");
if (pstyle.hasParent())
dumpIt(*dynamic_cast<const ParagraphStyle*>(pstyle.parentStyle()), more + indent);
}
*/
static const bool legacy = true;
/*
static void layoutDropCap(GlyphLayout layout, double curX, double curY, double offsetX, double offsetY, double dropCapDrop)
{
}
*/
enum TabStatus {
TabNONE = 0,
TabLEFT = TabNONE,
TabRIGHT = 1,
TabPOINT = 2,
TabCOMMA = 3,
TabCENTER = 4
};
/**
fields which describe what type of tab is currently active
*/
struct TabControl {
bool active;
int status;
double xPos;
QChar fillChar;
GlyphLayout* tabGlyph;
};
struct LineSpec
{
qreal x;
qreal y;
qreal width;
qreal ascent;
qreal descent;
qreal colLeft;
int firstRun;
int lastRun;
qreal naturalWidth;
bool isFirstLine;
qreal height;
};
/**
fields which describe how the current line is placed into the frame
*/
struct LineControl {
LineSpec line;
QList<GlyphRun>& glyphRuns;
bool isEmpty;
int hyphenCount;
double colWidth;
double colGap;
double colLeft;
double colRight;
int column;
bool startOfCol;
bool hasDropCap;
bool afterOverflow;
bool addLine;
bool recalculateY;
bool lastInRowLine;
bool addLeftIndent;
bool wasFirstInRow;
double leftIndent;
double rightMargin;
double mustLineEnd;
int restartIndex; //index of glyph run where line computing should be restarted
int restartRowIndex; //index of glyph run where row of text is started
double restartX; //starting X position of line if must be restarted
double rowDesc;
double xPos;
double yPos;
int breakIndex;
double breakXPos;
double maxShrink;
double maxStretch;
ScribusDoc *doc;
/// remember frame dimensions and offsets
LineControl(double w, double h, const MarginStruct& extra, double lCorr, ScribusDoc* d, QList<GlyphRun>& runs, double colwidth, double colgap)
: glyphRuns(runs)
, hasDropCap(false)
, doc(d)
{
insets = extra;
lineCorr = lCorr;
frameWidth = w;
frameHeight = h;
column = 0;
colWidth = colwidth;
colGap = colgap;
hyphenCount = 0;
isEmpty = true;
colLeft = insets.left() + lineCorr;
colRight = colLeft + colWidth;
startOfCol = true;
afterOverflow = false;
addLine = false;
recalculateY = false;
lastInRowLine = false;
addLeftIndent = false;
wasFirstInRow = false;
leftIndent = 0.0;
rightMargin = 0.0;
mustLineEnd = false;
restartIndex = 0;
restartRowIndex = 0;
restartX = 0.0;
rowDesc = 0.0;
xPos = 0.0;
yPos = 0.0;
breakIndex = -1;
breakXPos = 0.0;
maxShrink = 0.0;
}
/// move position to next column
void nextColumn(TextLayout &textLayout)
{
startOfCol = true;
colLeft = (colWidth + colGap) * column + insets.left() + lineCorr;
textLayout.addColumn(colLeft, colWidth);
//now colRight is REAL column right edge
colRight = colLeft + colWidth;
if (legacy)
colRight += lineCorr;
xPos = colLeft;
yPos = insets.top() + lineCorr;
line.colLeft = colLeft;
}
bool isEndOfCol(double morespace = 0)
{
return yPos + morespace + insets.bottom() + lineCorr > frameHeight;
}
/**
init fields for a new line at current position
*/
void startLine(int firstRun)
{
isEmpty = true;
line.x = xPos;
line.y = yPos;
line.firstRun = firstRun;
line.lastRun = 0;
line.ascent = 0.0;
line.descent = 0.0;
line.width = 0.0;
line.naturalWidth = 0.0;
line.colLeft = colLeft;
breakIndex = -1;
breakXPos = 0.0;
maxShrink = 0.0;
maxStretch = 0.0;
leftIndent = 0.0;
rightMargin = 0.0;
rowDesc = 0.0;
}
/// called when glyphs are placed on the line
void rememberShrinkStretch(QChar ch, double wide, const ParagraphStyle& style)
{
if (SpecialChars::isExpandingSpace(ch))
maxShrink += (1 - style.minWordTracking()) * wide;
else
{
maxShrink += (1 - style.minGlyphExtension()) * wide;
}
maxStretch += (style.maxGlyphExtension() - 1) * wide;
}
/// called when a possible break is passed
void rememberBreak(int index, double pos, double morespace = 0)
{
if (pos > colRight - morespace)
{
// only look for the first break behind the right edge
//maxShrink = 0;
// check if we already have a better break
if (breakIndex >= 0)
{
double oldLooseness = qAbs(colRight - breakXPos);
double newLooseness = pos - colRight;
if (newLooseness >= oldLooseness)
return;
}
}
breakXPos = pos;
breakIndex = index;
}
/// called when a mandatory break is found
void breakLine(const ParagraphStyle& style, FirstLineOffsetPolicy offsetPolicy, int last)
{
breakIndex = last;
breakXPos = line.x;
for (int i = line.firstRun; i <= breakIndex; i++)
breakXPos += glyphRuns.at(i).width();
// #8194, #8717 : update line ascent and descent with sensible values
// so that endOfLine() returns correct result
updateHeightMetrics();
// #9060 : update line offset too
// updateLineOffset(itemText, style, offsetPolicy);
}
/// use the last remembered break to set line width and itemrange
void finishLine(double endX)
{
line.lastRun = breakIndex;
line.naturalWidth = breakXPos - line.x;
line.width = endX - line.x;
maxShrink = maxStretch = 0;
}
int restartRow(bool recalcY)
{
if (recalcY)
yPos++;
recalculateY = recalcY;
xPos = restartX = colLeft;
startLine(restartRowIndex);
addLeftIndent = true;
afterOverflow = false;
return restartRowIndex - 1;
}
int restartLine(bool recalcY, bool add)
{
recalculateY = recalcY;
addLine = add;
xPos = restartX;
startLine(restartIndex);
afterOverflow = false;
return restartIndex - 1;
}
bool isEndOfLine(double moreSpace = 0)
{
bool res;
if (legacy)
res = ceil(xPos + lineCorr - maxShrink) + ceil(moreSpace) >= floor(colRight);
else
res = ceil(xPos - maxShrink) + ceil(moreSpace) >= floor(colRight);
return res;
}
/// Keep old endOfLine code for reference
/*double endOfLine_old(const QRegion& shape, const QTransform& pf2, double morespace, int Yasc, int Ydesc)
{
// if we aren't restricted further, we'll end at this maxX:
double maxX = colRight - morespace;
if (legacy) maxX -= lineCorr;
double StartX = floor(qMax(line.x, qMin(maxX,breakXPos-maxShrink-1))-1);
int xPos = static_cast<int>(ceil(maxX));
QPoint pt12 (xPos, Yasc);
QPoint pt22 (xPos, Ydesc);
QRect pt(pt12,pt22);
QRegion region;
double EndX2 = StartX;
double Interval = 0.25;
do {
int xP = static_cast<int>(ceil(EndX2 + morespace));
pt.moveTopLeft(QPoint(xP, Yasc));
region = QRegion(pf2.mapToPolygon(pt)).subtracted(shape);
if (!region.isEmpty())
break;
EndX2 += Interval;
} while ((EndX2 < maxX) && region.isEmpty());
return qMin(EndX2, maxX);
}*/
/// find x position where this line must end
double endOfLine(const QRegion& shape, double morespace, int yAsc, int yDesc)
{
// if we aren't restricted further, we'll end at this maxX:
double maxX = colRight - morespace;
if (legacy) maxX -= lineCorr;
double StartX = floor(qMax(line.x, qMin(maxX, breakXPos-maxShrink-1))-1);
StartX = qMax(0.0, StartX);
int xPos = static_cast<int>(ceil(maxX));
QPoint pt12 (xPos, yAsc);
QPoint pt22 (xPos, yDesc);
QPolygon p;
p.append (QPoint (StartX, yAsc));
p.append (QPoint (StartX, yDesc));
p.append (pt12);
p.append (pt22);
// check if something gets in the way
QRegion lineI = shape.intersected (p.boundingRect());
// if the intersection only has 1 rectangle, then nothing gets in the way
if (lineI.rectCount() == 1)
{
int cPos = static_cast<int>(ceil(StartX + morespace));
QRect cRect (QPoint(cPos, yAsc), QPoint(cPos, yDesc));
QRegion qr2 = QRegion(cRect).subtracted(shape);
if (qr2.isEmpty()) // qr2 == 0 <=> cRect subset of shape
{
QRect rect = lineI.rects().at(0);
double mx = qMax(rect.left(), rect.right()) /*- pf2.dx()*/;
int steps = static_cast<int>((mx - StartX - morespace - 2) / 0.25);
if (steps > 0)
{
StartX += steps * 0.25;
}
}
}
QRect pt(pt12, pt22);
double EndX2 = StartX;
double Interval = 0.25;
do {
int xP = static_cast<int>(ceil(EndX2 + morespace));
pt.moveTopLeft(QPoint(xP, yAsc));
if (!regionContainsRect(shape, pt))
break;
EndX2 += Interval;
} while ((EndX2 < maxX) && regionContainsRect(shape, pt));
/*double oldEndX2 = endOfLine_old(shape, pf2, morespace, yAsc, yDesc);
if (oldEndX2 != qMin(EndX2, maxX))
{
qDebug() << "Different EndX : " << oldEndX2 << " (old) " << EndX2 << " (new) ";
}*/
return qMin(EndX2, maxX);
}
void updateHeightMetrics()
{
line.ascent = line.descent = 0;
// Can happen when inserting a new line at end of text, WTF!
if (line.firstRun >= glyphRuns.length())
return;
const CharStyle& cStyle(glyphRuns.at(line.firstRun).style());
double scaleV = cStyle.scaleV() / 1000.0;
double offset = (cStyle.fontSize() / 10) * (cStyle.baselineOffset() / 1000.0);
line.ascent = cStyle.font().ascent(cStyle.fontSize()/10.00) * scaleV + offset;
line.descent = cStyle.font().descent(cStyle.fontSize()/10.00) * scaleV - offset;
}
// yPos should not be changed when all line is already calculated - at new y position there can be overflow!!!
// edit: can't happen as it should only move upwards, and this is covered by the calculations done.
//void updateLineOffset(const StoryText& itemText, const ParagraphStyle& style, FirstLineOffsetPolicy offsetPolicy)
//{
// if (charsInLine <= 0)
// return;
// if ((!hasDropCap) && (startOfCol) && (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing))
// {
// //FIXME: use glyphs, not chars
// double firstasce = itemText.charStyle(line.firstChar).font().ascent(itemText.charStyle(line.firstChar).fontSize() / 10.0);
// double adj (0.0);
// double currasce (this->getLineAscent(itemText));
// if (offsetPolicy == FLOPRealGlyphHeight)
// {
// adj = firstasce - currasce;
// }
// else if (offsetPolicy == FLOPFontAscent)
// {
// adj = 0.0;
// }
// else if (offsetPolicy == FLOPLineSpacing)
// {
// adj = firstasce - style.lineSpacing();
// }
// line.ascent = currasce;
// line.y -= adj;
// yPos -= adj;
// }
// else if ((!startOfCol) && (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing))
// {
// QChar ch = itemText.text(line.firstChar);
// double firstasce = style.lineSpacing();
// double currasce = getLineHeight(itemText);
// double adj = firstasce - currasce;
// qDebug() << QString("move2 line %1.. down by %2").arg(current.line.firstChar).arg(-adj);
// line.ascent = currasce;
// line.y -= adj;
// yPos -= adj;
// }
//}
LineBox* createLineBox()
{
LineBox* result = new LineBox();
result->moveTo(line.x - colLeft, line.y);
result->setWidth(line.width);
result->setAscent(line.ascent);
result->setDescent(line.descent);
for (int i = line.firstRun; i <= line.lastRun; ++i)
{
addBox(result, glyphRuns.at(i));
}
return result;
}
void addBox(LineBox *lineBox, const GlyphRun& run)
{
Box* result;
if (run.object())
{
result = new ObjectBox(run);
if (run.hasFlag(ScLayout_DropCap))
result->setAscent((run.object()->height() - run.object()->lineWidth()) * run.glyphs().first().scaleV - run.glyphs().first().yoffset);
else
result->setAscent(run.object()->height() - run.object()->lineWidth());
result->setDescent(0);
}
else
{
result = new GlyphBox(run);
result->setAscent(lineBox->ascent());
result->setDescent(lineBox->descent());
}
lineBox->addBox(result);
}
private:
double frameWidth;
double frameHeight;
MarginStruct insets;
double lineCorr;
};
/**
Clones the tab fill char as often as necssary after all distances are known
*/
static void fillInTabLeaders(LineControl & current)
{
// fill in tab leaders
for(int i = current.line.firstRun; i <= current.line.lastRun; i++)
{
GlyphRun& glyphRun = current.glyphRuns[i];
CharStyle charStyle(glyphRun.style());
if (glyphRun.hasFlag(ScLayout_TabLeaders))
{
GlyphLayout tglyph = glyphRun.glyphs().last();
double width = glyphRun.width();
double wt = charStyle.font().glyphWidth(tglyph.glyph, charStyle.fontSize() * tglyph.scaleV / 10.0);
int count = static_cast<int>(width / wt);
glyphRun.glyphs().clear();
for(int cx = 0; cx < count; ++cx)
{
GlyphLayout more = tglyph;
more.xadvance = width / count;
glyphRun.glyphs().append(more);
}
}
}
}
static bool checkCJK(QChar ch) {
unsigned int code = ch.unicode();
if ( (0x2E80 < code && code < 0x2EFF) || // CJK Radicals Supplement
(0x3000 < code && code < 0x303F) || // CJK Symbols and Punctuation
(0x3040 < code && code <= 0x30FF) || // Hiragana, Katakana
(0x31C0 < code && code < 0x31EF) || // CJK Strokes
(0x3200 < code && code < 0x32FF) || // Enclosed CJK Letters and Months
(0x3300 < code && code < 0x33FF) || // CJK Compatibility
(0x3400 < code && code < 0x4DBF) || // CJK Unified Ideographs Extension A
(0x4E00 < code && code < 0x9FFF) || // CJK Unified Ideographs
(0xF900 < code && code < 0xFAFF) || // CJK Compatibility Ideographs
(0xFE30 < code && code < 0xFE4F) || // CJK Compatibility Forms
(0x20000 < code && code < 0x2A6DF) || // CJK Unified Ideographs Extension B
(0x2A700 < code && code < 0x2B73F) || // CJK Unified Ideographs Extension C
(0x2B740 < code && code < 0x2B81F) || // CJK Unified Ideographs Extension D
(0x2F800 < code && code < 0x2FA1F) || // CJK Compatibility Ideographs Supplement
(0xFF01 < code && code < 0xFF0F) ||
(0xFF1A < code && code < 0xFF20) ||
(0xFF58 < code && code < 0xFFDC) ||
(code == 0x3000) ||
(code == 0x3002) ||
(code == 0x201C) ||
(code == 0x201D))
return true;
else
return false;
}
static bool allowedCJKBreakAfter(QChar ch) {
unsigned int code[] = {0x201C, 0x300C, 0xFF08, 0xFF3B, 0xFF5B, 0xFF5F, 0xFF62, 0xFF0D, 0};
for (int i = 0; code[i]; ++i)
if (code[i] == ch.unicode())
return false;
return true;
}
static int allowedCJKBreakBefore(QChar ch) {
unsigned int code[] =
{0x201D, 0x3001, 0x3002, 0x300D, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0xFF63, 0xFF64, 0};
for (int i = 0; code[i]; ++i)
if (code[i] == ch.unicode())
return false;
return true;
}
static bool implicitBreak(QChar f, QChar s) {
if (checkCJK(f) && checkCJK(s)) {
return allowedCJKBreakAfter(f) && allowedCJKBreakBefore(s);
} else
return false;
}
/// called when line length is known and line is to be justified
static void justifyLine(const ParagraphStyle& style, LineControl& curr)
{
double glyphNatural = 0;
double spaceNatural = 0;
double glyphExtension;
double spaceExtension;
int spaceInsertion = 0;
double imSpace = -1;
for (int i = curr.line.firstRun; i <= curr.line.lastRun; ++i)
{
GlyphRun glyphrun = curr.glyphRuns[i];
if (!glyphrun.hasFlag(ScLayout_ExpandingSpace))
{
glyphNatural += glyphrun.width();
}
else if ( !glyphrun.hasFlag(ScLayout_SuppressSpace) )
{
spaceNatural += glyphrun.width();
if (imSpace < 0.0 || imSpace > glyphrun.width())
imSpace = glyphrun.width();
}
if (i != curr.line.firstRun && glyphrun.hasFlag(ScLayout_ImplicitSpace))
{
spaceInsertion += 1;
}
}
imSpace /= 2;
// decision: prio 1: stretch glyph; prio 2: insert spaces; prio 3: stretch spaces
if (curr.line.width < spaceNatural + glyphNatural * style.minGlyphExtension() && spaceNatural > 0)
{
glyphExtension = style.minGlyphExtension() - 1;
spaceExtension = (curr.line.width - glyphNatural * (1+glyphExtension) ) / spaceNatural - 1;
imSpace = 0;
}
else if (curr.line.width < spaceNatural + glyphNatural * style.maxGlyphExtension() && glyphNatural > 0)
{
spaceExtension = 0;
glyphExtension = (curr.line.width - spaceNatural) / glyphNatural - 1;
imSpace = 0;
}
else
{
glyphExtension = style.maxGlyphExtension() - 1;
if (spaceInsertion) {
double remaining = curr.line.width - glyphNatural * (1 + glyphExtension) - spaceNatural;
if (imSpace > 0) {
if (remaining / spaceInsertion < imSpace) {
imSpace = remaining / spaceInsertion;
spaceExtension = 0;
} else {
spaceExtension = (remaining + spaceNatural) / (spaceNatural + spaceInsertion * imSpace) - 1;
imSpace *= spaceExtension + 1;
}
} else {
imSpace = remaining / spaceInsertion;
spaceExtension = 0;
}
} else {
if (spaceNatural > 0)
spaceExtension = (curr.line.width - glyphNatural * (1+glyphExtension) ) / spaceNatural - 1;
else
spaceExtension = 0;
}
}
double glyphScale = 1 + glyphExtension;
/*
qDebug() << QString("justify: line = %7 natural = %1 + %2 = %3 (%4); spaces + %5%%; min=%8; glyphs + %6%%; min=%9")
.arg(spaceNatural).arg(glyphNatural).arg(spaceNatural+glyphNatural).arg(line.naturalWidth)
.arg(spaceExtension).arg(glyphExtension).arg(line.width)
.arg(style.minWordTracking()).arg(style.minGlyphExtension());
*/
int startItem = curr.line.firstRun;
if (curr.glyphRuns[startItem].hasFlag(ScLayout_DropCap))
startItem++;
// distribute whitespace on spaces and glyphs
for (int i = startItem; i <= curr.line.lastRun; ++i)
{
GlyphRun& glyphrun = curr.glyphRuns[i];
double wide = glyphrun.width();
if (!glyphrun.hasFlag(ScLayout_ExpandingSpace))
{
for (int j = 0; j < glyphrun.glyphs().count(); ++j)
{
GlyphLayout& glyph = glyphrun.glyphs()[j];
glyph.xoffset *= glyphScale;
glyph.scaleH *= glyphScale;
}
}
else if (!glyphrun.hasFlag(ScLayout_SuppressSpace))
{
GlyphLayout& glyph = glyphrun.glyphs().last();
glyph.xadvance += (wide * spaceExtension) / glyph.scaleH;
}
if (i != curr.line.firstRun && glyphrun.hasFlag(ScLayout_ImplicitSpace))
{
GlyphRun& lastRun = curr.glyphRuns[i - 1];
lastRun.glyphs().last().xadvance += imSpace;
}
}
}
/// called when linelength is known and line is not justified
static void indentLine(const ParagraphStyle& style, LineControl& curr, double leftIndent)
{
if (curr.line.naturalWidth > curr.line.width)
{
justifyLine(style, curr);
}
if (leftIndent > 0)
{
curr.line.x += leftIndent;
curr.line.width -= leftIndent;
}
}
//defined but not used
///// calculate how much the first char should stick out to the left
//static double opticalLeftMargin(const StoryText& itemText, const LineSpec& line)
//{
// int b = line.firstChar;
// while (b < line.lastChar && (itemText.flags(b) & ScLayout_SuppressSpace))
// ++b;
//
// double chs = itemText.charStyle(b).fontSize() * (itemText.charStyle(b).scaleH() / 1000.0);
// QChar chr = itemText.text(b);
// double leftCorr = itemText.charStyle(b).font().realCharWidth(chr, chs / 10.0);
// if (QString("'´`").indexOf(chr) >= 0
// || chr == QChar(0x2018) // quote 6
// || chr == QChar(0x2019) // quote 9
// || chr == QChar(0x201a) // lower quote 9
// || chr == QChar(0x201b) // upper reversed 9 6
// || chr == QChar(0x2039) // single guillemet <
// || chr == QChar(0x203a) // single guillemet >
// )
// leftCorr *= -0.7;
// else if (QString("\"").indexOf(chr) >= 0
// || chr == QChar(0x00ab) // guillemet <<
// || chr == QChar(0x00bb) // guillemet >>
// || chr == QChar(0x201c) // quote 66
// || chr == QChar(0x201d) // quote 99
// || chr == QChar(0x201e) // lower quote 99
// || chr == QChar(0x201f) // upper reversed 99
// )
// leftCorr *= -0.5;
// else {
// leftCorr = itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0, chr);
// leftCorr -= itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0);
//// double leftCorr2 = itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0, chr);
//// leftCorr2 -= itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0);
//// leftCorr = qMin(leftCorr, leftCorr2);
// }
// return leftCorr;
//}
/// calculate how much the last char should stick out to the right
static double opticalRightMargin(const StoryText& itemText, const QList<GlyphRun> runs, const LineSpec& line)
{
int b = line.lastRun;
while (b > line.firstRun &&
(SpecialChars::isBreakingSpace(itemText.text(runs[b].firstChar())) ||
SpecialChars::isBreak(itemText.text(runs[b].firstChar())))
)
--b;
if (b >= line.firstRun)
{
const CharStyle& chStyle(runs[b].style());
double chs = chStyle.fontSize() * (chStyle.scaleH() / 1000.0);
QChar chr = runs[b].hasFlag(ScLayout_SoftHyphenVisible) ?
QChar('-') : itemText.text(runs[b].firstChar());
double rightCorr = chStyle.font().realCharWidth(chr, chs / 10.0);
if (QString("-,.`´'~").indexOf(chr) >= 0
|| chr == QChar(0x2018)
|| chr == QChar(0x2019)
|| chr == QChar(0x201a)
|| chr == QChar(0x201b)
|| chr == QChar(0x2039)
|| chr == QChar(0x203a)
|| chr == QChar(0x2032) // PRIME
)
rightCorr *= 0.7;
else if (QString(";:\"").indexOf(chr) >= 0
|| chr == QChar(0x00ab)
|| chr == QChar(0x00bb)
|| chr == QChar(0x201c)
|| chr == QChar(0x201d)
|| chr == QChar(0x201e)
|| chr == QChar(0x201f)
|| chr == QChar(0x2013) // EN DASH
|| chr == QChar(0x2033) // double prime
)
rightCorr *= 0.5;
else {
rightCorr = chStyle.font().charWidth(chr, chs / 10.0);
rightCorr -= chStyle.font().charWidth(chr, chs / 10.0, QChar('.'));
}
return rightCorr;
}
return 0.0;
}
static double findRealOverflowEnd(const QRegion& shape, QRect pt, double maxX)
{
while (!regionContainsRect(shape, pt) && pt.right() < maxX)
pt.translate(1, 0);
if (pt.right() >= maxX)
return maxX;
return pt.left() + 0.5;
}
static double adjustToBaselineGrid (const LineControl &control, PageItem *item, int OwnPage)
{
double by = item->yPos();
if (OwnPage != -1)
by = by - item->doc()->Pages->at(OwnPage)->yOffset();
int ol1 = qRound((by + control.yPos - item->doc()->guidesPrefs().offsetBaselineGrid) * 10000.0);
int ol2 = static_cast<int>(ol1 / item->doc()->guidesPrefs().valueBaselineGrid);
// qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil( ol2 / 10000.0 ) * item->doc()->typographicSettings.valueBaselineGrid + item->doc()->typographicSettings.offsetBaselineGrid - by);
return ceil( ol2 / 10000.0 ) * item->doc()->guidesPrefs().valueBaselineGrid + item->doc()->guidesPrefs().offsetBaselineGrid - by;
}
static double nextAutoTab (const LineControl ¤t, PageItem *item)
{
double dtw = item->doc()->itemToolPrefs().textTabWidth;
if (current.xPos == current.colLeft)
return current.colLeft + dtw;
double res = current.colLeft + ceil ((current.xPos - current.colLeft) / dtw) * dtw;
if (res == current.xPos) res += dtw;
return res;
}
//cezaryece: I remove static statement as this function is used also by PageItem_NoteFrame
double calculateLineSpacing (const ParagraphStyle &style, PageItem *item)
{
if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
{
double autoLS = static_cast<double>(item->doc()->typographicPrefs().autoLineSpacing) / 100.0;
return (style.charStyle().font().height(style.charStyle().fontSize() / 10.0) * autoLS);
}
if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
return item->doc()->guidesPrefs().valueBaselineGrid;
return style.lineSpacing();
}
// This assumes that layout() ran on the previous page and set the incomplete* vars
// It also clears the incomplete* vars, and changes the starting position for this frame
// The incomplete* vars are used to ensure that we don't run into an endless loop
// This setup won't cause any problems, as the only way for the starting position to change
// back is if the previous frame's layout() runs again, but if it does, it also sets the
// incomplete* vars for us
bool PageItem_TextFrame::moveLinesFromPreviousFrame ()
{
PageItem_TextFrame* prev = dynamic_cast<PageItem_TextFrame*>(BackBox);
if (!prev) return false;
if (!prev->incompleteLines) return false; // no incomplete lines - nothing to do
int pos = textLayout.endOfFrame()-1;
QChar lastChar = itemText.text (pos);
// qDebug()<<"pos is"<<pos<<", length is"<<itemText.length()<<", incomplete is "<<prev->incompleteLines;
if ((pos != itemText.length()-1) && (!SpecialChars::isBreak (lastChar, true)))
return false; // the paragraph isn't ending yet
int lines = textLayout.lines(); // lines added to the current frame
ParagraphStyle style = itemText.paragraphStyle (pos);
int need = style.keepLinesEnd () + 1;
int prevneed = style.keepLinesStart () + 1;
if (lines >= need) {
prev->incompleteLines = 0; // so that further paragraphs don't pull anything
return false; // we have enough lines
}
// too few lines - we need to pull some from the previous page
int pull = need - lines;
// if pulling the lines would lead to the original frame having too few, pull the whole paragraph
if (prev->incompleteLines - pull < prevneed)
pull = prev->incompleteLines;
// qDebug()<<"pulling"<<pull<<"lines;
// Okay, move the starting/ending character
int startingPos = prev->incompletePositions[prev->incompleteLines - pull];
for (int i = 0; i < pull; ++i)
prev->textLayout.removeLastLine();
firstChar = prev->MaxChars = startingPos;
// keep the remaining incomplete lines flagged as such
// this ensures that if pulling one line won't be enough, the subsequent call to layout() will pull more
prev->incompleteLines -= pull;
return true;
}
// called at the end of a frame or column
void PageItem_TextFrame::adjustParagraphEndings ()
{
// More text to go - let's apply paragraph flowing options - orphans/widows, etc
int pos = textLayout.endOfFrame() - 1;
if (pos >= itemText.length() - 1) return;
ParagraphStyle style = itemText.paragraphStyle (pos);
int paragraphStart = itemText.prevParagraph (pos) + 1;
QChar lastChar = itemText.text (pos);
bool keepWithNext = style.keepWithNext() && (lastChar == SpecialChars::PARSEP);
if (keepWithNext || (!SpecialChars::isBreak (lastChar, true))) {
// paragraph continues in the next frame, or needs to be kept with the next one
// check how many lines are in this frame
int lineStart = textLayout.startOfLine (pos);
incompleteLines = 1;
incompletePositions.prepend (lineStart);
while (lineStart > paragraphStart) {
lineStart = textLayout.startOfLine (lineStart - 1);
incompleteLines++;
incompletePositions.prepend (lineStart);
}
int need = style.keepLinesStart () + 1;
if (style.keepTogether()) need = incompleteLines;
int pull = 0;
if (style.keepTogether() || (incompleteLines < need)) pull = incompleteLines;
// if we need to keep it with the next one, pull one line. Next frame layouting
// will pull more from us if it proves necessary.
if (keepWithNext && (!pull)) pull = 1;
if (pull) {
// push this paragraph to the next frame
for (int i = 0; i < pull; ++i)
textLayout.removeLastLine();
MaxChars = incompletePositions[incompleteLines-pull];
incompleteLines = 0;
incompletePositions.clear();
}
}
}
void PageItem_TextFrame::layout()
{
// qDebug()<<"==Layout==" << itemName() ;
// printBacktrace(24);
if (BackBox != NULL && BackBox->invalid) {
// qDebug("textframe: len=%d, going back", itemText.length());
// Why that invalid = false here? Calling prevInChain->layout() does
// not ensure that this box will be layouted
// invalid = false;
PageItem_TextFrame* prevInChain = dynamic_cast<PageItem_TextFrame*>(BackBox);
while (prevInChain && prevInChain->invalid)
{
if (!prevInChain->BackBox || !prevInChain->BackBox->invalid)
break;
prevInChain = dynamic_cast<PageItem_TextFrame*>(prevInChain->BackBox);
}
if (prevInChain && prevInChain->invalid)
prevInChain->layout();
// #9592 : warning, BackBox->layout() may not layout BackBox next box
if (!invalid)
return;
}
else if (!invalid && OnMasterPage.isEmpty()) {
// qDebug() << QString("textframe: len=%1, invalid=%2 OnMasterPage=%3: no relayout").arg(itemText.length()).arg(invalid).arg(OnMasterPage);
return;
}
if (invalid && BackBox == NULL)
firstChar = 0;
// qDebug() << QString("textframe(%1,%2): len=%3, start relayout at %4").arg(Xpos).arg(Ypos).arg(itemText.length()).arg(firstInFrame());
QPoint pt1, pt2;
QRect pt;
double chs, chsd = 0;
double EndX, OFs;
ParagraphStyle style;
int opticalMargins = ParagraphStyle::OM_None;
bool outs = false;
bool goNoRoom = false;
bool goNextColumn = false;
TabControl tabs;
tabs.active = false; // RTab
tabs.status = TabNONE; // TabCode
tabs.tabGlyph = 0; // was int charIndex ~ StartRT
tabs.xPos = 0; // RTabX
QList<ParagraphStyle::TabRecord> tTabValues;
tTabValues.clear();
bool BulNumMode = false; //when bullet or counter should be inserted
bool DropCmode = false, FlopBaseline = false;
double desc=0, asce=0, realAsce=0, realDesc = 0, offset = 0;
double maxDY=0, maxDX=0;
double DropCapDrop = 0;
int DropLines = 0;
int DropLinesCount = 0;
textLayout.clear();
incompleteLines = 0;
incompletePositions.clear();
double lineCorr = 0;
if (lineColor() != CommonStrings::None)
lineCorr = m_lineWidth / 2.0;
//hold Y position of last computed line of text (with glyphs descent)
//for moving next line if glyphs are higher than that
double lastLineY = 0;
QMap<int, Mark*> noteMarksPosMap; //maping notes marks and its position in text
// dump styles
/*
for (int i=0; i < itemText.nrOfParagraphs(); ++i) {
const ParagraphStyle& pstyle(itemText.paragraphStyle(itemText.endOfParagraph(i)));
qDebug("par %d:", i);
dumpIt(pstyle);
}
qDebug() << "default:";
dumpIt(itemText.defaultStyle());
*/
setShadow();
int itLen = itemText.length();
//fast validate empty frames
if (itLen == 0 || firstInFrame() == itLen)
{
PageItem* next = this;
while (next != NULL)
{
next->invalid = false;
next = next->nextInChain();
}
// TODO layout() shouldn't delete any frame here, as it breaks any loop
// over the doc->Items or doc->MasterItems lists when done with indexes
// see Bug #12685 for an example
if (!isNoteFrame() && m_Doc->notesChanged() && !m_notesFramesMap.isEmpty())
{ //if notes are used
UndoManager::instance()->setUndoEnabled(false);
QList<PageItem_NoteFrame*> delList;
foreach (PageItem_NoteFrame* nF, m_notesFramesMap.keys())
{
if (nF->notesList().isEmpty() && !nF->isAutoNoteFrame())
delList.append(nF);
}
while (!delList.isEmpty())
m_Doc->delNoteFrame(delList.takeFirst(), false);
UndoManager::instance()->setUndoEnabled(true);
}
return;
}
if ((itLen != 0)) // || (NextBox != 0))
{
// determine layout area
m_availableRegion = calcAvailableRegion();
if (m_availableRegion.isEmpty())
{
MaxChars = firstInFrame();
goto NoRoom;
}
if (imageFlippedH() || imageFlippedV())
{
QTransform matrix;
if (imageFlippedH())
{
matrix.translate(m_width, 0);
matrix.scale(-1, 1);
}
if (imageFlippedV())
{
matrix.translate(0, m_height);
matrix.scale(1, -1);
}
m_availableRegion = matrix.map(m_availableRegion);
}
QList<GlyphRun> glyphRuns;
TextShaper shaper(this, firstInFrame());
LineControl current(m_width, m_height, m_textDistanceMargins, lineCorr, m_Doc, glyphRuns, columnWidth(), ColGap);
current.nextColumn(textLayout);
lastLineY = m_textDistanceMargins.top();
//automatic line spacing factor (calculated once)
double autoLS = static_cast<double>(m_Doc->typographicPrefs().autoLineSpacing) / 100.0;
// find start of first line
if (firstInFrame() < itLen)
{
CharStyle cstyle = itemText.charStyle(firstInFrame());
style = itemText.paragraphStyle(firstInFrame());
style.setLineSpacing (calculateLineSpacing (style, this));
// qDebug() << QString("style @0: %1 -- %2, %4/%5 char: %3").arg(style.leftMargin()).arg(style.rightMargin())
// .arg(style.charStyle().asString()).arg(style.name()).arg(style.parent()?style.parent()->name():"");
if (style.hasDropCap())
{
chs = calculateLineSpacing (style, this) * style.dropCapLines() * 10;
}
else
chs = cstyle.fontSize();
desc = -cstyle.font().descent(chs / 10.0);
current.yPos = m_textDistanceMargins.top() + lineCorr;
// qDebug() << QString("first line at y=%1").arg(current.yPos);
}
else // empty itemText:
{
desc = -itemText.defaultStyle().charStyle().font().descent(itemText.defaultStyle().charStyle().fontSize() / 10.0);
current.yPos = itemText.defaultStyle().lineSpacing() + m_textDistanceMargins.top() + lineCorr - desc;
}
current.startLine(0);
outs = false;
OFs = 0;
MaxChars = 0;
double realEnd = 0;
current.restartIndex = current.restartRowIndex = 0;
current.afterOverflow = false;
current.addLine = false;
current.recalculateY = true;
current.lastInRowLine = false;
current.addLeftIndent = true;
current.wasFirstInRow = false;
current.leftIndent = 0;
current.rightMargin = 0.0;
current.mustLineEnd = current.colRight;
current.restartX = 0;
int lastStat = 0, curStat = 0;
//why emit invalidating signals each time text is changed by appling styles?
//this speed up layouting in case of using notes marks and drop caps
itemText.blockSignals(true);
setMaxY(-1);
double maxYAsc = 0.0, maxYDesc = 0.0;
int regionMinY = 0, regionMaxY= 0;
double autoLeftIndent = 0.0;
for (int i = 0; shaper.hasRun(i); ++i)
{
GlyphRun newRun = shaper.runAt(i);
if (i >= glyphRuns.count())
glyphRuns.append(newRun);
else
glyphRuns[i] = newRun;
GlyphRun& currentRun = glyphRuns[i];
if (currentRun.glyphs().isEmpty())
continue;
int a = glyphRuns[i].firstChar();
bool HasObject = itemText.hasObject(a);
PageItem* currentObject = itemText.object(a);
bool HasMark = itemText.hasMark(a);
const StyleFlag& effects = itemText.charStyle(a).effects();
if (effects & ScStyle_UnderlineWords)
{
if (itemText.text(a).isSpace())
glyphRuns[i].clearFlag(ScLayout_Underlined);
else
glyphRuns[i].setFlag(ScLayout_Underlined);
}
if (HasMark)
{
Mark* mark = itemText.mark(a);
//store mark pointer and position in text
if (mark->isType(MARKNoteMasterType))
noteMarksPosMap.insert(a, mark);
}
BulNumMode = false;
if (a==0 || itemText.text(a-1) == SpecialChars::PARSEP)
{
autoLeftIndent = 0.0;
style = itemText.paragraphStyle(a);
if (style.hasBullet() || style.hasNum())
{
BulNumMode = true;
}
}
if (current.isEmpty)
opticalMargins = style.opticalMargins();
//-->#13490
if (isNoteFrame() && !HasMark)
{
StyleFlag s(itemText.charStyle(a).effects());
s &= ~ScStyle_Superscript;
CharStyle haveSuperscript;
haveSuperscript.setFeatures(s.featureList());
itemText.applyCharStyle(a, 1, haveSuperscript);
}
//--<#13490
CharStyle charStyle = ((itemText.text(a) != SpecialChars::PARSEP) ? itemText.charStyle(a) : style.charStyle());
curStat = SpecialChars::getCJKAttr(itemText.text(a));
//set style for paragraph effects
if (a == 0 || itemText.text(a-1) == SpecialChars::PARSEP)
{
if (style.hasDropCap() || style.hasBullet() || style.hasNum())
{
const QString& curParent(style.hasParent() ? style.parent() : style.name());
CharStyle newStyle(charStyle);
if (style.peCharStyleName().isEmpty())
newStyle.setParent(m_Doc->paragraphStyle(curParent).charStyle().name());
else if (charStyle.name() != style.peCharStyleName())
newStyle.setParent(m_Doc->charStyle(style.peCharStyleName()).name());
charStyle.setStyle(newStyle);
itemText.setCharStyle(a, 1 , charStyle);
}
else if (!style.peCharStyleName().isEmpty())
//par effect is cleared but is set dcCharStyleName = clear drop cap char style
{
const QString& curParent(style.hasParent() ? style.parent() : style.name());
charStyle.eraseCharStyle(m_Doc->charStyle(style.peCharStyleName()));
charStyle.setParent(m_Doc->paragraphStyle(curParent).charStyle().name());
itemText.setCharStyle(a, 1,charStyle);
}
}
double hlcsize10 = charStyle.fontSize() / 10.0;
double scaleV = charStyle.scaleV() / 1000.0;
double scaleH = charStyle.scaleH() / 1000.0;
double offset = hlcsize10 * (charStyle.baselineOffset() / 1000.0);
style.setLineSpacing (calculateLineSpacing (style, this));
FlopBaseline = (current.startOfCol && firstLineOffset() == FLOPBaselineGrid);
// find out about par gap and dropcap
if (a == firstInFrame())
{
if (a == 0 || itemText.text(a-1) == SpecialChars::PARSEP)
{
if (itemText.text(a) != SpecialChars::PARSEP)
{
DropCmode = style.hasDropCap();
if (DropCmode)
DropLines = style.dropCapLines();
}
else
DropCmode = false;
current.line.isFirstLine = true;
}
}
const ScFace font = charStyle.font();
glyphRuns[i].clearFlag(ScLayout_DropCap);
glyphRuns[i].clearFlag(ScLayout_SoftHyphenVisible);
// No space at begin of line,
if (legacy)
{
// unless at begin of par (eeks)
if ( (current.isEmpty) && (SpecialChars::isBreakingSpace(itemText.text(a)))
&& (a > 0 && ! SpecialChars::isBreak(itemText.text(a-1)))
&& ! (a > 0 && SpecialChars::isBreakingSpace(itemText.text(a-1))
&& (!glyphRuns[i - 1].hasFlag(ScLayout_SuppressSpace))))
{
glyphRuns[i].setFlag(ScLayout_SuppressSpace);
glyphRuns[i].glyphs().first().xadvance = 0;
continue;
}
else
glyphRuns[i].clearFlag(ScLayout_SuppressSpace);
}
else // from 134 on use NBSPACE for this effect
{
if ( current.isEmpty && (SpecialChars::isBreakingSpace(itemText.text(a)) || itemText.text(a).isSpace()))
{
glyphRuns[i].setFlag(ScLayout_SuppressSpace);
glyphRuns[i].glyphs()[0].xadvance = 0;
continue;
}
else
glyphRuns[i].clearFlag(ScLayout_SuppressSpace);
}
if (current.isEmpty)
{
if (style.rightMargin() == 0)
{
//addLine = true;
current.rightMargin = 0.0;
}
else
{
if (current.lastInRowLine)
current.rightMargin = style.rightMargin();
else
current.rightMargin = 0.0;
}
current.breakIndex = -1;
if (current.startOfCol && !current.afterOverflow && current.recalculateY)
current.yPos = qMax(current.yPos, m_textDistanceMargins.top());
// more about par gap and dropcaps
if ((a > firstInFrame() && itemText.text(a-1) == SpecialChars::PARSEP) || (a == 0 && BackBox == 0 && current.startOfCol))
{
if (!current.afterOverflow && current.recalculateY && !current.startOfCol)
current.yPos += style.gapBefore();
DropCapDrop = 0;
if (itemText.text(a) != SpecialChars::PARSEP)
DropCmode = style.hasDropCap();
else
DropCmode = false;
if (DropCmode && !current.afterOverflow)
{
DropLines = style.dropCapLines();
DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
// qDebug() << QString("dropcapdrop: y=%1+%2").arg(current.yPos).arg(DropCapDrop);
}
current.line.isFirstLine = true;
}
}
// find charsize factors
if (DropCmode)
{
// DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
// FIXME : we should ensure that fonts are loaded before calls to layout()
// ScFace::realCharHeight()/Ascent() ensure font is loaded thanks to an indirect call to char2CMap()
// ScFace::ascent() can be called safely afterwards
//text height, width, ascent and descent should be calculated for whole text provided by ScText in current position
//and that may be more than one char (variable text for example)
double realCharHeight = 0.0, realCharAscent = 0.0;
foreach (GlyphLayout gl, glyphRuns[i].glyphs()) {
GlyphMetrics gm = font.glyphBBox(gl.glyph);
realCharHeight = qMax(realCharHeight, gm.ascent + gm.descent);
realCharAscent = qMax(realCharAscent, gm.ascent);
}
double fontAscent = font.ascent(style.charStyle().fontSize() / 10.0);
if (realCharHeight == 0.0)
realCharHeight = font.height(style.charStyle().fontSize() / 10.0);
if (realCharAscent == 0.0)
realCharAscent = fontAscent;
chsd = (10 * ((DropCapDrop + fontAscent) / realCharHeight));
chs = (10 * ((DropCapDrop + fontAscent) / realCharAscent));
glyphRuns[i].setFlag(ScLayout_DropCap);
// glyphRuns[i].glyphs().first().yoffset += DropCapDrop;
if (HasObject)
{
chs = qRound((currentObject->height() + currentObject->lineWidth()) * 10);
chsd = qRound((currentObject->height() + currentObject->lineWidth()) * 10);
}
}
else // ! dropCapMode
{
if (HasObject)
chs = qRound((currentObject->height() + currentObject->lineWidth()) * 10);
else
chs = charStyle.fontSize();
}
// set StartOfLine
if (current.isEmpty)
{
glyphRuns[i].setFlag(ScLayout_StartOfLine);
}
else
{
glyphRuns[i].clearFlag(ScLayout_StartOfLine);
}
// glyphs->yadvance = 0;
GlyphLayout& firstGlyph = glyphRuns[i].glyphs().first();
GlyphLayout& lastGlyph = glyphRuns[i].glyphs().last();
// Apply cjk kerning
// TODO: cjk spacing and kerning should be done in layoutGlyphs!
if (shaper.hasRun(i + 1))
{
GlyphRun nextRun = shaper.runAt(i + 1);
// change xadvance, xoffset according to JIS X4051
int nextStat = SpecialChars::getCJKAttr(itemText.text(nextRun.firstChar()));
if (curStat != 0)
{ // current char is CJK
if (nextStat == 0 && !SpecialChars::isBreakingSpace(itemText.text(nextRun.firstChar()))){
switch(curStat & SpecialChars::CJK_CHAR_MASK){
case SpecialChars::CJK_KANJI:
case SpecialChars::CJK_KANA:
case SpecialChars::CJK_NOTOP:
lastGlyph.xadvance += charStyle.fontSize() / 10 / 4;
}
} else { // next char is CJK, too
switch(curStat & SpecialChars::CJK_CHAR_MASK){
case SpecialChars::CJK_FENCE_END:
switch(nextStat & SpecialChars::CJK_CHAR_MASK){
case SpecialChars::CJK_FENCE_BEGIN:
case SpecialChars::CJK_FENCE_END:
case SpecialChars::CJK_COMMA:
case SpecialChars::CJK_PERIOD:
case SpecialChars::CJK_MIDPOINT:
lastGlyph.xadvance -= charStyle.fontSize() / 10 / 2;
}
break;
case SpecialChars::CJK_COMMA:
case SpecialChars::CJK_PERIOD:
switch(nextStat & SpecialChars::CJK_CHAR_MASK){
case SpecialChars::CJK_FENCE_BEGIN:
case SpecialChars::CJK_FENCE_END:
lastGlyph.xadvance -= charStyle.fontSize() / 10 / 2;;
}
break;
case SpecialChars::CJK_MIDPOINT:
switch(nextStat & SpecialChars::CJK_CHAR_MASK){
case SpecialChars::CJK_FENCE_BEGIN:
lastGlyph.xadvance -= charStyle.fontSize() / 10 / 2;
}
break;
case SpecialChars::CJK_FENCE_BEGIN:
int prevStat = 0;
if (i == current.line.firstRun){ // first char of the line
prevStat = SpecialChars::CJK_FENCE_BEGIN;
} else if (i != 0) {
prevStat = SpecialChars::getCJKAttr(itemText.text(glyphRuns[i - 1].lastChar())) & SpecialChars::CJK_CHAR_MASK;
}
if (prevStat == SpecialChars::CJK_FENCE_BEGIN){
lastGlyph.xadvance -= charStyle.fontSize() / 10 / 2;
lastGlyph.xoffset -= charStyle.fontSize() / 10 / 2;
}
break;
}
}
} else { // current char is not CJK
if (nextStat != 0 && !SpecialChars::isBreakingSpace(itemText.text(glyphRuns[i].lastChar()))){
switch(nextStat & SpecialChars::CJK_CHAR_MASK){
case SpecialChars::CJK_KANJI:
case SpecialChars::CJK_KANA:
case SpecialChars::CJK_NOTOP:
// use the size of the current char instead of the next one
lastGlyph.xadvance += charStyle.fontSize() / 10 / 4;
}
}
}
}
// find out width, ascent and descent of char
double wide = glyphRuns[i].width();
if (DropCmode)
{
// drop caps are wider...
if (HasObject)
{
double itemHeight = currentObject->height() + currentObject->lineWidth();
if (itemHeight == 0)
itemHeight = font.height(style.charStyle().fontSize() / 10.0);
asce = currentObject->height() + currentObject->lineWidth();
wide = currentObject->width() + currentObject->lineWidth();
realAsce = calculateLineSpacing (style, this) * DropLines;
firstGlyph.scaleH /= firstGlyph.scaleV;
firstGlyph.scaleV = (realAsce / itemHeight);
firstGlyph.scaleH *= firstGlyph.scaleV;
wide *= firstGlyph.scaleH;
}
else
{
double realCharHeight = 0.0;
wide = 0.0; realAsce = 0.0;
foreach (GlyphLayout gl, glyphRuns[i].glyphs()) {
GlyphMetrics gm;
gm = font.glyphBBox(gl.glyph, charStyle.fontSize() / 10.0);
realCharHeight = qMax(realCharHeight, gm.ascent + gm.descent);
gm = font.glyphBBox(gl.glyph, chsd / 10.0);
realAsce = qMax(realAsce, gm.ascent + gm.descent);
wide += gm.width;
}
wide = (wide * scaleH) + (1 - scaleH);
realAsce = realAsce * scaleV + offset;
if (realCharHeight == 0)
realCharHeight = font.height(style.charStyle().fontSize() / 10.0);
asce = font.ascent(hlcsize10);
// qDebug() QString("dropcaps pre: chsd=%1 realCharHeight = %2 chstr=%3").arg(chsd).arg(asce).arg(chstr2[0]);
firstGlyph.scaleH /= firstGlyph.scaleV;
firstGlyph.scaleV = (realAsce / realCharHeight);
firstGlyph.scaleH *= firstGlyph.scaleV;
firstGlyph.xoffset -= 0.5; //drop caps are always to far from column left edge
}
// This is to mimic pre-boxes branches in case first character of paragraph is a space
// If we don't do this, paragraph offset will not apply correctly to first line
if ((firstGlyph.scaleH == 0.0) || (firstGlyph.scaleV == 0.0))
{
firstGlyph.scaleH = firstGlyph.scaleV = 1.0;
wide = 0.0;
}
firstGlyph.xadvance = wide / firstGlyph.scaleH;
desc = realDesc = 0;
}
else // !DropCMode
{
// find ascent / descent
if (HasObject)
desc = realDesc = 0;
else
{
if (itemText.text(a) != SpecialChars::OBJECT)
{
foreach (GlyphLayout gl, glyphRuns[i].glyphs()) {
GlyphMetrics gm = font.glyphBBox(gl.glyph, hlcsize10);
realDesc = qMax(realDesc, gm.descent * scaleV - offset);
realAsce = gm.ascent;
}
}
desc = -font.descent(hlcsize10);
current.rememberShrinkStretch(itemText.text(a), wide, style);
}
if (HasObject)
{
asce = currentObject->height() + currentObject->lineWidth();
realAsce = asce * scaleV + offset;
}
else
{
asce = font.ascent(hlcsize10);
if (HasMark && !BulNumMode)
realAsce = asce * scaleV + offset;
else
{
foreach (GlyphLayout gl, glyphRuns[i].glyphs())
realAsce = qMax(realAsce, font.glyphBBox(gl.glyph, hlcsize10).ascent * scaleV + offset);
}
}
}
// if (BulNumMode)
// hl->glyph.last()->xadvance += style.parEffectOffset();
//check for Y position at beginning of line
if (current.isEmpty && !current.afterOverflow)
{
if (current.recalculateY)
{
//if top of column Y position depends on first line offset
if (current.startOfCol)
{
lastLineY = qMax(lastLineY, m_textDistanceMargins.top() + lineCorr);
//fix for proper rendering first empty line (only with PARSEP)
if (itemText.text(a) == SpecialChars::PARSEP)
current.yPos += style.lineSpacing();
if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing || FlopBaseline)
{
if (current.yPos <= lastLineY)
current.yPos = lastLineY +1;
double by = m_yPos;
if (OwnPage != -1)
by = m_yPos - m_Doc->Pages->at(OwnPage)->yOffset();
int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
current.yPos = ceil( ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
}
else if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
{
if (firstLineOffset() == FLOPRealGlyphHeight)
{
if (DropCmode)
current.yPos += asce;
else
current.yPos += realAsce;
}
else if (firstLineOffset() == FLOPLineSpacing)
current.yPos += style.lineSpacing();
else
current.yPos += asce;
}
}
else
{
if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
{
current.yPos += m_Doc->guidesPrefs().valueBaselineGrid;
double by = m_yPos;
if (OwnPage != -1)
by = m_yPos - m_Doc->Pages->at(OwnPage)->yOffset();
int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
current.yPos = ceil( ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
}
else
current.yPos += style.lineSpacing();
}
if (DropCmode)
current.yPos += DropCapDrop;
}
//set left indentation
current.leftIndent = 0.0;
if (current.addLeftIndent && (maxDX == 0 || DropCmode || BulNumMode))
{
current.leftIndent = style.leftMargin() + autoLeftIndent;
if (a==0 || (a > 0 && (itemText.text(a-1) == SpecialChars::PARSEP)))
{
current.leftIndent += style.firstIndent();
if (BulNumMode || DropCmode)
{
if (style.parEffectIndent())
{
current.leftIndent -= style.parEffectOffset() + wide;
if (current.leftIndent < 0.0)
{
autoLeftIndent = abs(current.leftIndent);
current.leftIndent = 0.0;
}
}
}
}
current.addLeftIndent = false;
}
}
current.recalculateY = true;
maxYAsc = 0.0, maxYDesc = 0.0;
double addAsce = 0.0;
if (current.startOfCol)
{
if (DropCmode)
addAsce = qMax(realAsce, asce + offset);
else
addAsce = asce + offset;
if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
{
if (firstLineOffset() == FLOPRealGlyphHeight)
addAsce = realAsce;
else if (firstLineOffset() == FLOPLineSpacing)
addAsce = style.lineSpacing();
}
maxYAsc = current.yPos - addAsce;
}
else
maxYAsc = current.yPos - realAsce;
//fix for glyphs with negative realAsce value
maxYAsc = qMax(maxYAsc, 0.0);
maxYDesc = current.yPos + realDesc;
if (true||style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
{
regionMinY = static_cast<int>(floor(maxYAsc));
regionMaxY = static_cast<int>(floor(maxYDesc));
}
else // #11727, #11628, etc.
{
regionMinY = static_cast<int>(qMax(0.0, floor(current.yPos - (asce + offset))));
regionMaxY = static_cast<int>(floor(current.yPos + desc));
}
if (current.isEmpty && !current.afterOverflow)
{
//start a new line
goNoRoom = false;
// find line`s start
pt1 = QPoint(static_cast<int>(floor(current.xPos)), regionMinY);
pt2 = QPoint(static_cast<int>(floor(current.xPos + (style.minGlyphExtension() * wide))), regionMaxY -1);
pt = QRect(pt1, pt2);
realEnd = 0;
//check if there is overflow at start of line, if so jump behind it and check again
double Xpos, Xend;
bool done = false;
bool newColumn = false;
//FIX ME - that should be paragraph style`s properties
//if set then indent is add to possible line start point (after overflow)
//if not then indent is calculated from column left edge
//if you don't agree that adding indent to overflow should be default behaviour
//then change it to false
bool addIndent2overflow = false; // should be addIndent2Overflow = style.addIndent2Overlow();
bool addFirstIndent2overflow = true; // should be addFirstIndent2Overflow = style.addFirstIndent2Overlow();
//if first line indent is negative and left indent should not be added to overflow
//then don't add first line ident either
if ((style.firstIndent() < 0) && !addIndent2overflow)
addFirstIndent2overflow = false;
while (!done)
{
Xpos = current.xPos + (addIndent2overflow ? 0 : current.leftIndent);
Xend = current.xPos + current.leftIndent;
//check if in indent any overflow occurs
while (Xpos <= Xend && Xpos < current.colRight)
{
pt.moveTopLeft(QPoint(static_cast<int>(floor(Xpos)), regionMinY));
if (!regionContainsRect(m_availableRegion, pt))
{
Xpos = current.xPos = realEnd = findRealOverflowEnd(m_availableRegion, pt, current.colRight);
Xend = current.xPos + (addIndent2overflow ? current.leftIndent : 0);
//for first paragraph`s line - if first line offset should be added
if ( addFirstIndent2overflow && (a==0 || (a > 0 && (itemText.text(a-1) == SpecialChars::PARSEP))))
Xend += style.firstIndent();
}
else
Xpos++;
}
current.xPos = Xend;
done = true;
if (current.isEndOfLine((style.minGlyphExtension() * wide) + current.rightMargin))
{
// new line
current.xPos = qMax(current.colLeft, maxDX);
if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing || FlopBaseline)
{
current.yPos++;
double by = m_yPos;
if (OwnPage != -1)
by = m_yPos - m_Doc->Pages->at(OwnPage)->yOffset();
int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
current.yPos = ceil( ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
}
else if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
current.yPos += (current.startOfCol ? 1 : style.lineSpacing());
else
current.yPos += (current.startOfCol ? 1 : style.lineSpacing());
lastLineY = maxYAsc;
if (current.startOfCol)
{
// double addAsce;
// if (DropCmode)
// addAsce = qMax(realAsce, asce + offset);
// else
// addAsce = asce + offset;
// if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
// {
// if (firstLineOffset() == FLOPRealGlyphHeight)
// addAsce = realAsce;
// else if (firstLineOffset() == FLOPLineSpacing)
// addAsce = style.lineSpacing() + offset;
// }
maxYAsc = current.yPos - addAsce;
}
else
maxYAsc = current.yPos - realAsce;
maxYDesc = current.yPos + realDesc;
if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
{
regionMinY = static_cast<int>(floor(maxYAsc));
regionMaxY = static_cast<int>(floor(maxYDesc));
}
else // #11727, #11628, etc.
{
regionMinY = static_cast<int>(qMax(0.0, floor(current.yPos - (asce + offset))));
regionMaxY = static_cast<int>(floor(current.yPos + desc));
}
pt.moveTopLeft(QPoint(static_cast<int>(floor(current.xPos)), regionMinY));
done = false;
}
if (current.isEndOfCol(realDesc))
{
current.column++;
if (current.column < Cols)
{
newColumn = true;
break;
}
else
{
MaxChars = a;
goto NoRoom;
}
}
}
if (newColumn)
{
current.nextColumn(textLayout);
current.mustLineEnd = current.colRight;
current.restartX = current.xPos;
lastLineY = current.yPos;
current.rowDesc = 0;
i--;
current.recalculateY = true;
current.addLeftIndent = true;
continue;
}
current.line.x = current.restartX = current.xPos;
current.line.y = current.yPos;
// if (glyphRuns[current.line.firstRun].hasFlag(ScLayout_DropCap))
// current.line.y -= DropCapDrop;
}
//check if line must start at new Y position due to current glyph height or previous line descent
if (!SpecialChars::isBreak(itemText.text(a), true)
&& !SpecialChars::isBreakingSpace(itemText.text(a))
&& !SpecialChars::isExpandingSpace(itemText.text(a))
&& itemText.text(a) != SpecialChars::TAB)
{
double diff = 0;
if (current.startOfCol || DropCmode)
diff = realAsce - (current.yPos - lastLineY);
else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
{
if (HasObject)
diff = (currentObject->height() + currentObject->lineWidth()) * scaleV + offset - (current.yPos - lastLineY);
else
diff = font.capHeight(hlcsize10) * scaleV + offset - (current.yPos - lastLineY);
}
else
{
if (HasObject)
diff = (currentObject->height() + currentObject->lineWidth()) * scaleV + offset - (current.yPos - lastLineY);
}
if (diff >= 1 || (!DropCmode && diff > 0))
{
if (current.hasDropCap && DropLinesCount == 0)
{
current.hasDropCap = false;
current.yPos = maxDY;
maxDX = 0;
}
int linesDrop = 0;
if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing || FlopBaseline)
{
linesDrop = ceil(diff / m_Doc->guidesPrefs().valueBaselineGrid);
current.yPos += m_Doc->guidesPrefs().valueBaselineGrid * linesDrop;
maxDX = 0;
}
else /*if (current.startOfCol)*/
{
//FIX ME - that is ugly hack I must made, because simply expression
//current.yPos += diff; stop working, don't know why (compiler bug?)
float YPOS = (float) current.yPos + (float) diff + 0.01;
current.yPos = (double) YPOS;
if (current.hasDropCap && diff > DropCapDrop)
{
current.hasDropCap = false;
}
}
if (current.hasDropCap && linesDrop > 0 && DropLinesCount > 1)
{
DropLinesCount += linesDrop;
if (DropLinesCount >= DropLines)
{
current.hasDropCap = false;
maxDX = 0;
maxDY = 0;
}
}
i = current.restartRow(false);
continue;
}
}
// right tab stuff
if (tabs.active)
{
if (((itemText.text(a) == '.') && (tabs.status == TabPOINT)) || ((itemText.text(a) == ',') && (tabs.status == TabCOMMA)) || (itemText.text(a) == SpecialChars::TAB))
{
tabs.active = false;
tabs.status = TabNONE;
}
}
// tab positioning
if (itemText.text(a) == SpecialChars::TAB)
{
wide = 1;
if (tabs.active)
tabs.active = false;
else // ???
{
tabs.xPos = current.xPos;
tTabValues = style.tabValues();
if (tTabValues.isEmpty())
{
current.xPos = nextAutoTab (current, this);
tabs.status = TabNONE;
tabs.active = false;
}
else
{
double tCurX = current.xPos;
double oCurX = current.xPos - current.colLeft + wide;
for (int yg = static_cast<int>(tTabValues.count()-1); yg > -1; yg--)
{
if (oCurX < tTabValues.at(yg).tabPosition)
{
tabs.status = static_cast<int>(tTabValues.at(yg).tabType);
tabs.fillChar = tTabValues.at(yg).tabFillChar;
current.xPos = current.colLeft + tTabValues.at(yg).tabPosition;
}
}
tabs.active = (tabs.status != TabLEFT);
if (current.xPos == tCurX) // no more tabs found
{
current.xPos = nextAutoTab (current, this);
tabs.status = TabNONE;
tabs.active = false;
tabs.fillChar = QChar();
}
// remember fill char
if (!tabs.fillChar.isNull())
{
glyphRuns[i].setFlag(ScLayout_TabLeaders);
GlyphLayout tglyph;
tglyph.glyph = font.char2CMap(tabs.fillChar);
tglyph.yoffset = firstGlyph.yoffset;
tglyph.scaleV = tglyph.scaleH = chs / charStyle.fontSize();
tglyph.xadvance = 0;
glyphRuns[i].glyphs().append(tglyph);
}
}
current.xPos -= (legacy ? 1.0 : 0.0);
firstGlyph.xadvance = (current.xPos + wide - tabs.xPos) / firstGlyph.scaleH;
tabs.tabGlyph = &firstGlyph;
}
}
// remember y pos
if (DropCmode)
{
double yoffset = 0.0;
foreach (GlyphLayout gl, glyphRuns[i].glyphs())
yoffset = qMax(yoffset, font.glyphBBox(gl.glyph, chsd / 10.0).descent);
firstGlyph.yoffset -= yoffset;
}
// remember x pos
double breakPos = current.xPos;
if (!tabs.active) // normal case
{
current.xPos += wide;
}
else if (tabs.active && tabs.status == TabCENTER) // center tab
{
current.xPos += wide / 2;
current.xPos = qMax(current.xPos, current.colLeft);
}
else // other tabs.active
{
current.xPos = qMax(current.xPos, current.colLeft);
}
// remember possible break
if ( (SpecialChars::isBreakingSpace(itemText.text(a)) || itemText.text(a) == SpecialChars::TAB))
{
if ( a == firstInFrame() || !SpecialChars::isBreakingSpace(itemText.text(a-1)) )
{
current.rememberBreak(i, breakPos, style.rightMargin());
}
}
if (HasObject)
current.rememberBreak(i, breakPos, style.rightMargin());
// CJK break
if (i > current.line.firstRun)
{ // not the first char
if ((lastStat == 0) && (curStat == 0))
{ // both non-CJK
// do nothing
} else {
// non-CJK char does not have CJK_NOBREAK_AFTER/CJK_NOBREAK_BEFORE
if ((lastStat & SpecialChars::CJK_NOBREAK_AFTER) == 0 &&
(curStat & SpecialChars::CJK_NOBREAK_BEFORE) == 0){
current.rememberBreak(i-1, breakPos, style.rightMargin());
}
}
}
lastStat = curStat;
//check against space before PARSEP
/*if (SpecialChars::isBreakingSpace(hl->ch) && (a + 1 < itemText.length()) && (itemText.item(a+1)->ch == SpecialChars::PARSEP))
{
a++;
hl = itemText.item(a);
}*/
// test if end of line reached
double overflowWidth = 0.0;
double hyphWidth = 0.0;
bool inOverflow = false;
if (glyphRuns[i].hasFlag(ScLayout_HyphenationPossible) || itemText.text(a) == SpecialChars::SHYPHEN)
hyphWidth = font.charWidth('-', hlcsize10) * (charStyle.scaleH() / 1000.0);
if ((current.isEndOfLine(style.rightMargin() + hyphWidth)) || current.isEndOfCol(realDesc) || SpecialChars::isBreak(itemText.text(a), Cols > 1) || (current.xPos - current.maxShrink + hyphWidth) >= current.mustLineEnd)
{
//end of row reached - right column, end of column, break char or line must end
if (current.isEmpty && !current.afterOverflow && !SpecialChars::isBreak(itemText.text(a), Cols > 1))
{
//no glyphs in line, so start new row
if (SpecialChars::isBreak(itemText.text(a), Cols > 1))
current.restartRowIndex = i + 1;
i = current.restartRow(true);
inOverflow = false;
outs = false;
continue;
}
//there we have some glyphs
if (current.breakIndex < 0)
{
//force break
if (!SpecialChars::isBreak(itemText.text(a), Cols > 1))
{
//force line end at previouse glyph
i--;
a = glyphRuns[i].firstChar();
current.mustLineEnd = current.line.x;
}
current.breakLine(style, firstLineOffset(), i);
}
if (!current.addLine && !current.lastInRowLine && current.afterOverflow)
{
//we reach right edge of column
//if we are after overflow area we have to go back and try to insert text before it
//if we have some text here - insert text WITHOUT right margin
//if there is no place for text - insert text WITH right margin and end line
current.lastInRowLine = false;
if (current.line.firstRun == current.restartIndex)
current.lastInRowLine = true;
if (current.hasDropCap && DropLinesCount == 0 && current.restartIndex == current.restartRowIndex)
{
current.hasDropCap = false;
maxDX = 0;
current.yPos = maxDY;
}
i = current.restartLine(false,true);
outs = false;
inOverflow = false;
continue;
}
outs = true;
current.addLine = true;
current.lastInRowLine = true;
current.rightMargin = style.rightMargin();
if (current.isEndOfCol(realDesc))
goNextColumn = true;
}
else
{
int charStart, charEnd;
if (current.isEmpty)
{
charStart = static_cast<int>(floor(current.line.x));
charEnd = static_cast<int>(ceil(current.xPos));
}
else
{
charStart = static_cast<int>(qMax(floor(current.xPos - current.maxShrink - (style.minGlyphExtension() * wide)),0.0));
charEnd = static_cast<int>(ceil(current.xPos - current.maxShrink));
}
if (legacy &&
(((itemText.text(a) == '-' || glyphRuns[i].hasFlag(ScLayout_HyphenationPossible)) && (current.hyphenCount < m_Doc->hyphConsecutiveLines() || m_Doc->hyphConsecutiveLines() == 0))
|| itemText.text(a) == SpecialChars::SHYPHEN))
{
if (glyphRuns[i].hasFlag(ScLayout_HyphenationPossible) || itemText.text(a) == SpecialChars::SHYPHEN)
{
pt1 = QPoint(charStart, regionMinY);
pt2 = QPoint(static_cast<int>(charEnd + hyphWidth), regionMaxY -1);
}
else
{
pt1 = QPoint(charStart, regionMinY);
pt2 = QPoint(charEnd, regionMaxY -1);
}
}
else if (!legacy && SpecialChars::isBreakingSpace(itemText.text(a)))
{
pt1 = QPoint(static_cast<int>(qMax(floor(breakPos - current.maxShrink - (style.minGlyphExtension() * wide)),0.0)), regionMinY);
pt2 = QPoint(charEnd, regionMaxY -1);
}
else
{
pt1 = QPoint(charStart, regionMinY);
pt2 = QPoint(charEnd, regionMaxY -1);
}
pt = QRect(pt1, pt2);
if (!regionContainsRect(m_availableRegion, pt))
{
realEnd = findRealOverflowEnd(m_availableRegion, pt, current.colRight);
outs = true;
}
else if (style.rightMargin() > 0.0)
{
if (current.lastInRowLine || current.xPos - current.maxShrink + style.rightMargin() > current.colRight - style.rightMargin())
//condition after || is for find overflows in right margin area
{
pt.translate(static_cast<int>(ceil(style.rightMargin())), 0);
if (!regionContainsRect(m_availableRegion, pt))
{
realEnd = findRealOverflowEnd(m_availableRegion, pt, current.colRight);
outs = true;
}
}
}
if (outs)
{
if (current.isEmpty)
{
current.line.x = current.xPos = realEnd;
i--;
current.startLine(i+1);
if (!current.wasFirstInRow)
current.addLeftIndent = true;
if (current.hasDropCap && DropLinesCount == 0 && !current.afterOverflow)
{
current.hasDropCap = false;
current.yPos = maxDY;
}
current.recalculateY = false;
outs = false;
continue;
}
if (current.breakIndex < 0)
{
i--;
a = glyphRuns[i].firstChar();
current.breakLine(style, firstLineOffset(), i);
}
//check if after overflow text can be placed
overflowWidth = realEnd - (current.xPos - current.maxShrink);
double newXAdd = overflowWidth - style.rightMargin() + style.minGlyphExtension() * wide + hyphWidth;
if (current.isEndOfLine(newXAdd) || current.xPos + newXAdd >= current.colRight || realEnd >= current.mustLineEnd)
{
if (!current.afterOverflow)
{
current.addLine = true;
current.lastInRowLine = true;
}
//line must end before overflov
if (!current.addLine && !current.lastInRowLine)
{
if (current.afterOverflow)
{
if (current.breakIndex < 0)
{
current.lastInRowLine = true;
current.mustLineEnd = current.line.x;
}
else if (current.line.firstRun == current.restartIndex)
current.lastInRowLine = true;
}
else
current.lastInRowLine = true;
if (current.hasDropCap && DropLinesCount == 0)
{
current.hasDropCap = false;
maxDX = 0;
}
i = current.restartLine(false, true);
inOverflow = false;
outs = false;
continue;
}
current.addLine = true;
current.rightMargin = style.rightMargin();
current.lastInRowLine = true;
}
else
inOverflow = true;
}
else
setMaxY(maxYDesc);
}
// hyphenation
if ((glyphRuns[i].hasFlag(ScLayout_HyphenationPossible)
|| itemText.text(a) == '-'
|| itemText.text(a) == SpecialChars::SHYPHEN)
&& (!outs) && ((i == 0) || !itemText.text(glyphRuns[i - 1].lastChar()).isSpace()) )
{
breakPos = current.xPos;
if (itemText.text(a) != '-')
{
breakPos += hyphWidth;
}
double rightHang = 0;
if (opticalMargins & ParagraphStyle::OM_RightHangingPunct) {
rightHang = 0.7 * hyphWidth;
}
if (legacy || (breakPos - rightHang < current.colRight - style.rightMargin()))
{
if ((current.hyphenCount < m_Doc->hyphConsecutiveLines()) || (m_Doc->hyphConsecutiveLines() == 0) || itemText.text(a) == SpecialChars::SHYPHEN)
{
current.rememberBreak(i, breakPos, style.rightMargin() + hyphWidth);
}
}
}
if ((itemText.text(a) == SpecialChars::FRAMEBREAK) && (a < itemText.length()-1))
goNoRoom = true;
if ((itemText.text(a) == SpecialChars::COLBREAK) && (Cols > 1))
goNextColumn = true;
if (a != firstInFrame() && implicitBreak(itemText.text(a - 1), itemText.text(a)))
current.rememberBreak(i -1, breakPos);
current.isEmpty = (i - current.line.firstRun + 1) == 0;
if (tabs.active)
{
double cen = 1;
if (tabs.status == TabCENTER)
cen = 2;
double newTabAdvance = tabs.tabGlyph->xadvance;
newTabAdvance -= wide / cen / tabs.tabGlyph->scaleH;
if (newTabAdvance >= 0) {
tabs.tabGlyph->xadvance = newTabAdvance;
}
else {
tabs.active = false;
tabs.status = TabNONE;
}
}
if ((DropCmode || BulNumMode) && !outs)
{
current.xPos += style.parEffectOffset();
lastGlyph.xadvance += style.parEffectOffset() / lastGlyph.scaleH;
if (DropCmode)
{
DropCmode = false;
DropLinesCount = 0;
maxDY = current.yPos;
current.hasDropCap = true;
maxDX = current.xPos;
double spacing = calculateLineSpacing (style, this);
current.yPos -= spacing * (DropLines-1);
if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
current.yPos = adjustToBaselineGrid (current, this, OwnPage);
current.recalculateY = false;
}
}
// end of line
if (outs)
{
tabs.active = false;
tabs.status = TabNONE;
if (SpecialChars::isBreak(itemText.text(a), Cols > 1))
{
// find end of line
current.breakLine(style, firstLineOffset(), i);
EndX = current.endOfLine(m_availableRegion, style.rightMargin(), regionMinY, regionMaxY);
current.finishLine(EndX);
//addLine = true;
assert(current.addLine);
//current.startOfCol = false;
//addLeftIndent = true;
if (itemText.text(a) == SpecialChars::PARSEP)
{
maxDX = 0;
if (current.hasDropCap)
{
current.yPos = maxDY;
maxDY = 0;
current.hasDropCap = false;
}
}
// if (style.alignment() != 0)
{
if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
current.line.width += opticalRightMargin(itemText, glyphRuns, current.line);
OFs = 0;
if (style.alignment() == ParagraphStyle::Rightaligned)
OFs = current.line.width - current.line.naturalWidth;
if (style.alignment() == ParagraphStyle::Centered)
OFs = (current.line.width - current.line.naturalWidth) / 2;
if (style.alignment() == ParagraphStyle::Justified)
OFs = 0;
if (style.alignment() == ParagraphStyle::Extended
|| (style.alignment() == ParagraphStyle::Justified
&& (itemText.text(a) == SpecialChars::LINEBREAK ||
itemText.text(a) == SpecialChars::FRAMEBREAK ||
itemText.text(a) == SpecialChars::COLBREAK)
&& (current.line.lastRun - 1 >= 0)
&& !itemText.text(glyphRuns[current.line.lastRun - 1].lastChar()).isSpace()))
{
justifyLine(style, current);
}
else
{
if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
current.line.naturalWidth += opticalRightMargin(itemText, glyphRuns, current.line);
double optiWidth = current.colRight - style.rightMargin() - style.lineSpacing()/2.0 - current.line.x;
if (current.line.naturalWidth > optiWidth)
current.line.width = qMax(current.line.width - current.maxShrink, optiWidth);
// simple offset
indentLine(style, current, OFs);
}
current.xPos = current.colRight;
}
}
else // outs -- last char went outside the columns (or into flow-around shape)
{
if (current.breakIndex >= 0)
{
i = current.breakIndex;
a = glyphRuns[i].firstChar();
}
assert( i >= 0 );
assert( i < glyphRuns.length() );
//glyphs = itemText.getGlyphs(a);
current.isEmpty = (i - current.line.firstRun + 1) == 0;
if (current.addLine)
{
// go back to last break position
style = itemText.paragraphStyle(a);
if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
style.setLineSpacing(font.height(hlcsize10) * autoLS);
else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
if (itemText.text(a) == ' ') {
glyphRuns[i].setFlag(ScLayout_SuppressSpace);
glyphRuns[i].glyphs()[0].xadvance = 0;
}
current.updateHeightMetrics();
//current.updateLineOffset(itemText, style, firstLineOffset());
//current.xPos = current.breakXPos;
EndX = current.endOfLine(m_availableRegion, current.rightMargin, regionMinY, regionMaxY);
current.finishLine(EndX);
hyphWidth = 0.0;
if (glyphRuns[i].hasFlag(ScLayout_HyphenationPossible) || itemText.text(a) == SpecialChars::SHYPHEN)
{
// insert hyphen
if (current.lastInRowLine)
//increase hyphen count only for hyphens a the end of text row, omit hyphens before overflow
current.hyphenCount++;
glyphRuns[i].setFlag(ScLayout_SoftHyphenVisible);
GlyphLayout hyphen;
hyphen.glyph = font.char2CMap(QChar('-'));
hyphen.xadvance = font.glyphBBox(hyphen.glyph, itemText.charStyle(a).fontSize() / 10.0).width;
hyphWidth = hyphen.xadvance * scaleH;
glyphRuns[i].glyphs().append(hyphen);
}
else
{
if (itemText.text(a) != '-')
current.hyphenCount = 0;
if (glyphRuns[i].hasFlag(ScLayout_SoftHyphenVisible))
{
glyphRuns[i].clearFlag(ScLayout_SoftHyphenVisible);
glyphRuns[i].glyphs().removeLast();
}
}
// Justification
if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
current.line.width += opticalRightMargin(itemText, glyphRuns, current.line);
// #12565: Right alignment of hyphens
// The additional character width has already been taken into account
// above via the line break position, so it's not necessary to increase
// the line natural width again:
// line 2604: breakPos = current.xPos;
// line 2605: if (currentCh != '-')
// line 2606: {
// line 2607: breakPos += hyphWidth;
// line 2608: }
/*else
current.line.naturalWidth += hyphWidth;*/
OFs = 0;
if (style.alignment() == ParagraphStyle::Rightaligned)
OFs = current.line.width - current.line.naturalWidth;
if (style.alignment() == ParagraphStyle::Centered)
OFs = (current.line.width - current.line.naturalWidth) / 2;
if ((style.alignment() == ParagraphStyle::Justified) || (style.alignment() == ParagraphStyle::Extended))
justifyLine(style, current);
else
{
if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
current.line.naturalWidth += opticalRightMargin(itemText, glyphRuns, current.line);
indentLine(style, current, OFs);
}
current.xPos = current.line.x + current.line.width;
}
}
if (inOverflow)
current.xPos = realEnd;
// line break and drop caps
// #11250: in case of a forced line break, we must not stop
// the drop cap layout process. This break case such as
// layout of poetry.
if (itemText.text(a) == SpecialChars::PARSEP && current.hasDropCap)
{
current.hasDropCap = false;
if (current.yPos < maxDY)
current.yPos = maxDY;
maxDX = 0;
}
// end of column
if (current.isEndOfCol(desc))
{
//check if really line extends bottom margin
current.updateHeightMetrics();
if (current.isEndOfCol(current.line.descent))
{
if (current.isEmpty || current.column+1 == Cols)
{
goNoRoom = true;
MaxChars = a + 1;
break;
}
goNextColumn = true;
}
else
setMaxY(maxYDesc);
}
else
setMaxY(maxYDesc);
if (current.line.firstRun <= current.line.lastRun && !current.isEmpty)
{
if (current.addLine && current.breakIndex >= 0)
{
// if (glyphRuns[current.line.firstRun].style().effects() & ScLayout_DropCap)
if (glyphRuns[current.line.firstRun].hasFlag(ScLayout_DropCap))
{
// put line back to top
current.line.y -= DropCapDrop;
glyphRuns[current.line.firstRun].glyphs().first().yoffset += DropCapDrop;
}
fillInTabLeaders(current);
//if right margin is set we temporally save line, not append it
textLayout.appendLine(current.createLineBox());
setMaxY(maxYDesc);
current.restartIndex = current.line.lastRun + 1;
i = current.line.lastRun;
a = glyphRuns[i].firstChar();
current.rowDesc = qMax(current.rowDesc,current.yPos + current.line.descent);
if (!current.lastInRowLine)
{
current.restartX = current.xPos;
if (current.addLeftIndent)
{
current.addLeftIndent = false;
current.wasFirstInRow = true;
}
}
}
else if (!inOverflow && !current.afterOverflow && current.lastInRowLine)
{
current.yPos++;
i = current.restartLine(true, true);
inOverflow = false;
outs = false;
current.mustLineEnd = current.colRight;
continue;
}
}
if (current.addLine && current.lastInRowLine)
{
current.recalculateY = true;
current.wasFirstInRow = false;
current.addLeftIndent = true;
inOverflow = false;
outs = false;
current.startOfCol = false;
current.restartX = current.xPos = qMax(maxDX, current.colLeft);
lastLineY = current.rowDesc;
if (current.hasDropCap)
{
++DropLinesCount;
if (DropLinesCount >= DropLines)
{
current.hasDropCap = false;
current.restartX = current.xPos = current.colLeft;
maxDX = 0;
}
}
lastLineY = current.rowDesc;
current.mustLineEnd = current.colRight;
current.restartRowIndex = current.restartIndex;
}
if ( SpecialChars::isBreak(itemText.text(a)) )
{
if (itemText.text(a) == SpecialChars::PARSEP)
current.yPos += style.gapAfter();
current.hyphenCount = 0;
}
if (inOverflow)
{
current.afterOverflow = true;
inOverflow = false;
current.addLeftIndent = false;
current.recalculateY = false;
}
else
{
current.afterOverflow = false;
}
outs = false;
current.addLine = false;
current.lastInRowLine = false;
// WTF does i+1 mean here, what if i is the last run we have!
current.startLine(i+1);
if (goNoRoom)
{
goNoRoom = false;
MaxChars = a+1;
goto NoRoom;
}
if (goNextColumn)
{
goNextColumn = false;
current.column++;
if (current.column < Cols)
{
if (firstLineOffset() == FLOPRealGlyphHeight)
asce = 0;
current.nextColumn(textLayout);
current.mustLineEnd = current.colRight;
current.addLeftIndent = true;
lastLineY = m_textDistanceMargins.top();
current.rowDesc = 0;
current.recalculateY = true;
}
else
{
MaxChars = a; // Always a+1???
if (itemText.text(a) == SpecialChars::COLBREAK)
++MaxChars;
goto NoRoom;
}
}
}
if (glyphRuns[i].lastChar() == itemText.length() - 1)
{
if (!current.afterOverflow || current.addLine)
{
current.addLine = true;
current.lastInRowLine = true;
current.breakLine(style, firstLineOffset(), i);
}
if (current.afterOverflow && !current.addLine)
{
if (current.restartIndex < current.line.firstRun)
{
i = current.restartLine(false,true);
continue;
}
else
current.breakLine(style, firstLineOffset(), i);
}
}
}
if (goNoRoom)
{
goNoRoom = false;
goto NoRoom;
}
// end of itemText
// now place the last line
if (!current.isEmpty)
{
int a = itemText.length()-1;
int i = glyphRuns.length() - 1;
current.breakLine(style, firstLineOffset(), i);
if (current.startOfCol)
{
double addAsce;
if (DropCmode)
addAsce = qMax(realAsce, asce + offset);
else
addAsce = asce + offset;
if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
{
if (firstLineOffset() == FLOPRealGlyphHeight)
addAsce = realAsce;
else if (firstLineOffset() == FLOPLineSpacing)
addAsce = style.lineSpacing() + offset;
}
maxYAsc = current.yPos - addAsce;
}
else
maxYAsc = current.yPos - realAsce;
maxYAsc = qMax(maxYAsc, 0.0);
maxYDesc = current.yPos + realDesc;
if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
{
regionMinY = static_cast<int>(floor(maxYAsc));
regionMaxY = static_cast<int>(floor(maxYDesc));
}
else // #11727, #11628, etc.
{
regionMinY = static_cast<int>(qMax(0.0, floor(current.yPos - (asce + offset))));
regionMaxY = static_cast<int>(floor(current.yPos + desc));
}
EndX = current.endOfLine(m_availableRegion, style.rightMargin(), regionMinY, regionMaxY);
current.finishLine(EndX);
if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
current.line.width += opticalRightMargin(itemText, glyphRuns, current.line);
OFs = 0;
if (style.alignment() == ParagraphStyle::Rightaligned)
OFs = current.line.width - current.line.naturalWidth;
if (style.alignment() == ParagraphStyle::Centered)
OFs = (current.line.width - current.line.naturalWidth) / 2;
if (style.alignment() == ParagraphStyle::Justified)
OFs = 0;
if (style.alignment() == ParagraphStyle::Extended
|| (style.alignment() == ParagraphStyle::Justified
&& (itemText.text(a) == SpecialChars::LINEBREAK ||
itemText.text(a) == SpecialChars::FRAMEBREAK ||
itemText.text(a) == SpecialChars::COLBREAK)
&& !itemText.text(a).isSpace()))
{
justifyLine(style, current);
}
else
{
if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
current.line.naturalWidth += opticalRightMargin(itemText, glyphRuns, current.line);
indentLine(style, current, OFs);
}
// if (glyphRuns[current.line.firstRun].style().effects() & ScLayout_DropCap)
if (glyphRuns[current.line.firstRun].hasFlag(ScLayout_DropCap))
{
// put line back to top
current.line.y -= DropCapDrop;
glyphRuns[current.line.firstRun].glyphs().first().yoffset += DropCapDrop;
}
fillInTabLeaders(current);
current.startOfCol = false;
goNextColumn = false;
textLayout.appendLine(current.createLineBox());
setMaxY(maxYDesc);
current.startOfCol = false;
if (moveLinesFromPreviousFrame ()) {
layout (); // line moving ensures that this won't be an endless loop
itemText.blockSignals(false);
return;
}
}
}
MaxChars = itemText.length();
if ((verticalAlign > 0) && (NextBox == NULL))
{
double hAdjust = height() - textLayout.box()->naturalHeight() - m_textDistanceMargins.bottom();
if (hAdjust > 0)
{
if (verticalAlign == 1)
hAdjust /= 2;
textLayout.box()->moveBy(0, hAdjust);
}
}
invalid = false;
if (!isNoteFrame() && (!m_Doc->notesList().isEmpty() || m_Doc->notesChanged()))
{ //if notes are used
UndoManager::instance()->setUndoEnabled(false);
NotesInFrameMap notesMap = updateNotesFrames(noteMarksPosMap);
if (notesMap != m_notesFramesMap)
{
updateNotesMarks(notesMap);
notesFramesLayout();
}
UndoManager::instance()->setUndoEnabled(true);
}
if (NextBox != NULL)
{
PageItem_TextFrame * nextFrame = dynamic_cast<PageItem_TextFrame*>(NextBox);
while (nextFrame)
{
nextFrame->invalid = true;
nextFrame->firstChar = MaxChars;
nextFrame = dynamic_cast<PageItem_TextFrame*>(nextFrame->NextBox);
}
}
itemText.blockSignals(false);
// qDebug("textframe: len=%d, done relayout", itemText.length());
return;
NoRoom:
invalid = false;
adjustParagraphEndings ();
if (!isNoteFrame() && (!m_Doc->notesList().isEmpty() || m_Doc->notesChanged()))
{
UndoManager::instance()->setUndoEnabled(false);
NotesInFrameMap notesMap = updateNotesFrames(noteMarksPosMap);
if (notesMap != m_notesFramesMap)
{
updateNotesMarks(notesMap);
notesFramesLayout();
}
UndoManager::instance()->setUndoEnabled(true);
}
PageItem_TextFrame * next = dynamic_cast<PageItem_TextFrame*>(NextBox);
if (next != NULL)
{
next->invalid = true;
next->firstChar = MaxChars;
if (itemText.cursorPosition() > signed(MaxChars))
{
int nCP = itemText.cursorPosition();
// CPos = MaxChars;
if (m_Doc->appMode == modeEdit)
{
// OwnPage->Deselect(true);
next->itemText.setCursorPosition( qMax(nCP, signed(MaxChars)) );
// Doc->currentPage = NextBox->OwnPage;
// NextBox->OwnPage->SelectItemNr(NextBox->ItemNr);
// qDebug("textframe: len=%d, leaving relayout in editmode && Tinput", itemText.length());
return;
}
}
// relayout next frame
// qDebug("textframe: len=%d, going to next", itemText.length());
next->layout();
}
// qDebug("textframe: len=%d, done relayout (no room %d)", itemText.length(), MaxChars);
itemText.blockSignals(false);
}
void PageItem_TextFrame::invalidateLayout(bool wholeChain)
{
//const bool wholeChain = true;
invalid = true;
if (wholeChain)
{
PageItem *prevFrame = this->prevInChain();
while (prevFrame != 0)
{
prevFrame->invalid = true;
prevFrame = prevFrame->prevInChain();
}
PageItem *nextFrame = this->nextInChain();
while (nextFrame != 0)
{
nextFrame->invalid = true;
nextFrame = nextFrame->nextInChain();
}
}
}
void PageItem_TextFrame::slotInvalidateLayout()
{
invalidateLayout(true);
}
bool PageItem_TextFrame::isValidChainFromBegin()
{
if (invalid)
return false;
if (BackBox == NULL)
return !invalid;
PageItem* prev = prevInChain();
while (prev != NULL)
{
if (prev->invalid)
return false;
prev = prev->prevInChain();
}
return true;
}
void PageItem_TextFrame::DrawObj_Item(ScPainter *p, QRectF cullingArea)
{
if (invalid)
{
if (isNoteFrame() && asNoteFrame()->deleteIt)
//do not layout notes frames which should be deleted
return;
layout();
}
if (invalid)
return;
if (m_Doc->RePos)
return;
QTransform pf2;
// tTabValues.clear();
p->save(); //SA1
if (!isEmbedded)
pf2.translate(m_xPos, m_yPos);
pf2.rotate(m_rotation);
if (!m_Doc->layerOutline(LayerID))
{
if ((fillColor() != CommonStrings::None) || (GrType != 0))
{
if (isAnnotation() && !((m_Doc->appMode == modeEdit) && (m_Doc->m_Selection->findItem(this) != -1)) && ((annotation().Type() == 2) || (annotation().Type() == 5) || (annotation().Type() == 6)))
{
if ((annotation().Feed() == 1) && annotation().IsOn())
p->setBrush(QColor(255 - fillQColor.red(), 255 - fillQColor.green(), 255 - fillQColor.blue(), fillQColor.alpha()));
}
p->setupPolygon(&PoLine);
p->fillPath();
}
}
MarginStruct savedTextDistanceMargins(m_textDistanceMargins);
if (isAnnotation() && !((m_Doc->appMode == modeEdit) && (m_Doc->m_Selection->findItem(this) != -1)) && (((annotation().Type() > 1) && (annotation().Type() < 11)) || (annotation().Type() > 12)))
{
if ((m_Doc->drawAsPreview && !m_Doc->editOnPreview) && ((annotation().Vis() == 1) || (annotation().Vis() == 3)))
{
p->restore();
return;
}
QColor fontColor;
SetQColor(&fontColor, itemText.defaultStyle().charStyle().fillColor(), itemText.defaultStyle().charStyle().fillShade());
double fontSize = itemText.defaultStyle().charStyle().fontSize() / 10.0;
QString fontName = itemText.defaultStyle().charStyle().font().family();
QString bmUtf16("");
QString cc;
if (!((itemText.length() == 1) && (itemText.text(0, 1) == QChar(13))))
{
for (uint d = 0; d < static_cast<uint>(itemText.length()); ++d)
{
if (d == 0)
{
const CharStyle& charStyle(itemText.charStyle(d));
SetQColor(&fontColor, charStyle.fillColor(), charStyle.fillShade());
fontSize = charStyle.fontSize() / 10.0;
fontName = charStyle.font().family();
}
cc = itemText.text(d, 1);
bmUtf16 += (cc == QChar(13) ? QChar(10) : cc);
}
}
p->save();
if ((annotation().Bwid() > 0) && (annotation().borderColor() != CommonStrings::None) && (annotation().Type() != Annotation::Text))
{
QColor tmp;
SetQColor(&tmp, annotation().borderColor(), 100);
int BStyle = annotation().Bsty();
if (annotation().IsOn())
{
if (annotation().Feed() == 2)
tmp = QColor(255 - tmp.red(), 255 - tmp.green(), 255 - tmp.blue(), tmp.alpha());
if (annotation().Feed() == 3)
BStyle = 4;
}
if (annotation().Type() == Annotation::RadioButton)
{
double bwh = annotation().Bwid() / 2.0;
if (annotation().IsOn())
{
if (BStyle == 4)
BStyle = 3;
else
BStyle = 4;
}
if ((BStyle == 0) || (BStyle == 1))
{
QPainterPath clp;
clp.addEllipse(QRectF(bwh, bwh, m_width - annotation().Bwid(), m_height - annotation().Bwid()));
FPointArray clpArr;
clpArr.fromQPainterPath(clp);
p->setupPolygon(&clpArr);
p->setPen(tmp, annotation().Bwid(), BStyle == 0 ? Qt::SolidLine : Qt::DashLine, Qt::FlatCap, Qt::MiterJoin);
p->setFillMode(ScPainter::None);
p->setStrokeMode(ScPainter::Solid);
p->strokePath();
}
else if (BStyle == 3)
p->drawShadeCircle(QRectF(0, 0, m_width, m_height), tmp, false, annotation().Bwid());
else if (BStyle == 4)
p->drawShadeCircle(QRectF(0, 0, m_width, m_height), tmp, true, annotation().Bwid());
}
else
{
if ((annotation().Type() == Annotation::Checkbox) && annotation().IsOn())
{
if (BStyle == 4)
BStyle = 3;
else
BStyle = 4;
}
if (BStyle == 3)
p->drawShadePanel(QRectF(0, 0, m_width, m_height), tmp, false, annotation().Bwid());
else if (BStyle == 4)
p->drawShadePanel(QRectF(0, 0, m_width, m_height), tmp, true, annotation().Bwid());
else
{
p->setPen(tmp, annotation().Bwid(), BStyle == 0 ? Qt::SolidLine : Qt::DashLine, Qt::FlatCap, Qt::MiterJoin);
p->setStrokeMode(ScPainter::Solid);
p->drawRect(0, 0, m_width, m_height);
}
}
}
if (annotation().Type() == Annotation::Button)
{
int wdt = annotation().Bwid();
QPainterPath clp;
clp.addRect(QRectF(wdt, wdt, m_width - (2 * wdt), m_height - (2 * wdt)));
FPointArray clpArr;
clpArr.fromQPainterPath(clp);
p->setupPolygon(&clpArr);
p->setClipPath();
if (!bmUtf16.isEmpty())
{
if ((annotation().Feed() == 1) && annotation().IsOn())
p->setPen(QColor(255 - fontColor.red(), 255 - fontColor.green(), 255 - fontColor.blue(), fontColor.alpha()));
else
p->setPen(fontColor);
p->setFont(QFont(fontName, fontSize));
p->drawText(QRectF(wdt, wdt, m_width - (2 * wdt), m_height - (2 * wdt)), bmUtf16, false);
}
if ((!Pfile.isEmpty()) && (imageIsAvailable) && (m_imageVisible) && (annotation().UseIcons()))
{
p->save();//SA2
p->setupPolygon(&PoLine);
p->setClipPath();
p->scale(m_imageXScale, m_imageYScale);
p->translate(m_imageXOffset*m_imageXScale, m_imageYOffset*m_imageYScale);
p->rotate(m_imageRotation);
if (pixm.width() > 0 && pixm.height() > 0)
p->drawImage(pixm.qImagePtr());
p->restore();//RE2
}
p->restore();
p->restore();
return;
}
else if (annotation().Type() == Annotation::Textfield)
{
int wdt = annotation().Bwid();
m_textDistanceMargins.set(wdt, wdt, wdt, wdt);
invalid = true;
layout();
}
else if (annotation().Type() == Annotation::RadioButton)
{
if (annotation().IsChecked())
{
QPainterPath clp2;
double siz = qMin(width(), height()) * 0.4;
QRectF sizR(0, 0, siz, siz);
sizR.moveCenter(QPointF(width() / 2.0, height() / 2.0));
clp2.addEllipse(sizR);
FPointArray clpArr2;
clpArr2.fromQPainterPath(clp2);
p->setBrush(fontColor);
p->setFillMode(ScPainter::Solid);
p->setupPolygon(&clpArr2);
p->fillPath();
}
p->restore();
p->restore();
return;
}
else if (annotation().Type() == Annotation::Checkbox)
{
if (annotation().IsChecked())
{
p->setBrush(fontColor);
p->setFillMode(ScPainter::Solid);
p->setStrokeMode(ScPainter::None);
QPainterPath chkPath;
if (annotation().ChkStil() == 0)
{
chkPath.moveTo(6.60156, 0);
chkPath.lineTo(6.7725, 0.244063);
chkPath.cubicTo(6.7725, 0.244063, 5.7275, 1.03031, 4.44813, 2.66609);
chkPath.cubicTo(4.44813, 2.66609, 3.16891, 4.30172, 2.49516, 5.72266);
chkPath.lineTo(2.13375, 5.96672);
chkPath.cubicTo(2.13375, 5.96672, 1.68453, 6.27922, 1.52344, 6.43062);
chkPath.cubicTo(1.52344, 6.43062, 1.46, 6.20109, 1.24516, 5.67875);
chkPath.lineTo(1.10844, 5.36125);
chkPath.cubicTo(1.10844, 5.36125, 0.815469, 4.67766, 0.563906, 4.35062);
chkPath.cubicTo(0.563906, 4.35062, 0.3125, 4.02344, 0, 3.91594);
chkPath.cubicTo(0, 3.91594, 0.527344, 3.35938, 0.966875, 3.35938);
chkPath.cubicTo(0.966875, 3.35938, 1.34281, 3.35938, 1.80172, 4.37984);
chkPath.lineTo(1.95312, 4.72172);
chkPath.cubicTo(1.95312, 4.72172, 2.77828, 3.33, 4.07219, 2.01656);
chkPath.cubicTo(4.07219, 2.01656, 5.36625, 0.703125, 6.60156, 0);
chkPath.closeSubpath();
}
else if (annotation().ChkStil() == 1)
{
chkPath.moveTo(2.42672, 3.30078);
chkPath.lineTo(2.35359, 3.40328);
chkPath.cubicTo(2.35359, 3.40328, 1.59187, 4.55563, 1.10359, 4.55563);
chkPath.cubicTo(1.10359, 4.55563, 0.542031, 4.55563, 0, 3.64266);
chkPath.cubicTo(0, 3.64266, 0.0732812, 3.6475, 0.1075, 3.6475);
chkPath.cubicTo(0.1075, 3.6475, 0.747031, 3.6475, 1.53328, 2.67094);
chkPath.lineTo(1.665, 2.50484);
chkPath.lineTo(1.49422, 2.31937);
chkPath.cubicTo(1.49422, 2.31937, 0.776406, 1.55766, 0.776406, 1.02547);
chkPath.cubicTo(0.776406, 1.02547, 0.776406, 0.590781, 1.5625, 0);
chkPath.cubicTo(1.5625, 0, 1.67484, 0.756875, 2.23141, 1.47469);
chkPath.lineTo(2.37797, 1.66016);
chkPath.lineTo(2.49516, 1.50875);
chkPath.cubicTo(2.49516, 1.50875, 3.31062, 0.46875, 3.97469, 0.46875);
chkPath.cubicTo(3.97469, 0.46875, 4.49219, 0.46875, 4.76078, 1.25484);
chkPath.cubicTo(4.76078, 1.25484, 4.68266, 1.24516, 4.64844, 1.24516);
chkPath.cubicTo(4.64844, 1.24516, 4.41406, 1.24516, 3.98922, 1.56016);
chkPath.cubicTo(3.98922, 1.56016, 3.56453, 1.875, 3.27156, 2.27047);
chkPath.lineTo(3.125, 2.47078);
chkPath.lineTo(3.26656, 2.6075);
chkPath.cubicTo(3.26656, 2.6075, 4.05281, 3.36922, 4.90719, 3.36922);
chkPath.cubicTo(4.90719, 3.36922, 4.44828, 4.29687, 3.95016, 4.29687);
chkPath.cubicTo(3.95016, 4.29687, 3.50094, 4.29687, 2.65141, 3.50594);
chkPath.lineTo(2.42672, 3.30078);
chkPath.closeSubpath();
}
else if (annotation().ChkStil() == 2)
{
chkPath.moveTo(0, 4.09187);
chkPath.lineTo(2.89062, 0);
chkPath.lineTo(5.78125, 4.09187);
chkPath.lineTo(2.89062, 8.17875);
chkPath.closeSubpath();
}
else if (annotation().ChkStil() == 3)
{
chkPath.moveTo(0, 2.89062);
chkPath.cubicTo(0, 2.89062, 0, 1.69437, 0.847187, 0.847187);
chkPath.cubicTo(0.847187, 0.847187, 1.69437, 0, 2.89062, 0);
chkPath.cubicTo(2.89062, 0, 4.08703, 0, 4.93656, 0.847187);
chkPath.cubicTo(4.93656, 0.847187, 5.78625, 1.69437, 5.78625, 2.89062);
chkPath.cubicTo(5.78625, 2.89062, 5.78625, 4.08688, 4.93656, 4.93406);
chkPath.cubicTo(4.93656, 4.93406, 4.08703, 5.78125, 2.89062, 5.78125);
chkPath.cubicTo(2.89062, 5.78125, 1.69437, 5.78125, 0.847187, 4.93406);
chkPath.cubicTo(0.847187, 4.93406, 0, 4.08688, 0, 2.89062);
chkPath.closeSubpath();
}
else if (annotation().ChkStil() == 4)
{
chkPath.moveTo(0, 2.49516);
chkPath.lineTo(2.62219, 2.49516);
chkPath.lineTo(3.43266, 0);
chkPath.lineTo(4.24328, 2.49516);
chkPath.lineTo(6.86531, 2.49516);
chkPath.lineTo(4.74609, 4.03812);
chkPath.lineTo(5.55672, 6.53313);
chkPath.lineTo(3.43266, 4.99016);
chkPath.lineTo(1.30859, 6.53313);
chkPath.lineTo(2.11922, 4.03812);
chkPath.closeSubpath();
}
else if (annotation().ChkStil() == 5)
{
chkPath.moveTo(0, 5.78125);
chkPath.lineTo(0, 0);
chkPath.lineTo(5.78125, 0);
chkPath.lineTo(5.78125, 5.78125);
chkPath.closeSubpath();
}
if (!chkPath.isEmpty())
{
QTransform mm;
mm.scale(fontSize / 10.0, fontSize / 10.0);
chkPath = mm.map(chkPath);
QRectF bb = chkPath.boundingRect();
QRectF bi = QRectF(0.0, 0.0, m_width, m_height);
double dx = bi.center().x() - (bb.width() / 2.0);
double dy = bi.center().y() - (bb.height() / 2.0);
p->translate(dx, dy);
FPointArray chArr;
chArr.fromQPainterPath(chkPath);
p->setupPolygon(&chArr);
p->fillPath();
}
p->restore();
p->restore();
return;
}
}
else if (annotation().Type() == Annotation::Combobox)
{
int wdt = annotation().Bwid();
if (m_width > 2 * wdt + 15)
{
if (!bmUtf16.isEmpty())
{
p->save();
QPainterPath clp;
clp.addRect(QRectF(wdt + 1, wdt + 1, m_width - (2 * wdt) - 17, m_height - (2 * wdt) - 2));
FPointArray clpArr;
clpArr.fromQPainterPath(clp);
p->setupPolygon(&clpArr);
p->setClipPath();
p->setPen(fontColor);
p->setFont(QFont(fontName, fontSize));
QStringList textList = bmUtf16.split("\n");
p->drawText(QRectF(wdt + 1, wdt + 1, m_width - (2 * wdt) - 17, m_height - (2 * wdt) - 2), textList[0], false, 1);
p->restore();
}
p->setFillMode(ScPainter::Solid);
p->setStrokeMode(ScPainter::None);
p->setBrush(QColor(200, 200, 200));
QRectF bi;
if ((annotation().Bsty() == 3) || (annotation().Bsty() == 4))
bi = QRectF(m_width - wdt - 15, wdt, 15, m_height - (2 * wdt));
else
bi = QRectF(m_width - (wdt / 2.0) - 15, wdt / 2.0, 15, m_height - wdt);
p->drawRect(bi.x(), bi.y(), bi.width(), bi.height());
QPainterPath chkPath;
chkPath.moveTo(bi.center().x() - 3, bi.center().y());
chkPath.lineTo(bi.center().x() + 3, bi.center().y());
chkPath.lineTo(bi.center().x(), bi.center().y() + 4);
chkPath.closeSubpath();
FPointArray chArr;
chArr.fromQPainterPath(chkPath);
p->setupPolygon(&chArr);
p->setBrush(QColor(0, 0, 0));
p->fillPath();
p->restore();
p->restore();
return;
}
}
else if (annotation().Type() == Annotation::Listbox)
{
int wdt = annotation().Bwid();
if (m_width > 2 * wdt + 15)
{
if (!bmUtf16.isEmpty())
{
p->save();
QPainterPath clp;
clp.addRect(QRectF(wdt + 1, wdt + 1, m_width - (2 * wdt) - 17, m_height - (2 * wdt) - 2));
FPointArray clpArr;
clpArr.fromQPainterPath(clp);
p->setupPolygon(&clpArr);
p->setClipPath();
p->setPen(fontColor);
p->setFont(QFont(fontName, fontSize));
p->drawText(QRectF(wdt + 1, wdt + 1, m_width - (2 * wdt) - 17, m_height - (2 * wdt) - 2), bmUtf16, false, 2);
p->restore();
}
p->restore();
p->restore();
return;
}
}
else if (annotation().Type() == Annotation::Text)
{
if (m_Doc->drawAsPreview && !m_Doc->editOnPreview)
{
if (annotation().IsOpen())
{
p->save();
p->translate(m_origAnnotPos.x() - xPos(), m_origAnnotPos.y() - yPos());
drawNoteIcon(p);
p->restore();
p->save();
double basX = 15;
double basY = 15;
QPainterPath clp;
clp.addRoundedRect(basX, basY, 250, 250, 5, 5);
p->setBrush(QColor(240, 240, 0));
p->setFillMode(ScPainter::Solid);
p->setStrokeMode(ScPainter::None);
FPointArray clpArr;
clpArr.fromQPainterPath(clp);
p->setupPolygon(&clpArr);
p->fillPath();
p->setPen(QColor(0, 0, 0), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
p->setBrush(QColor(255, 255, 255));
p->setStrokeMode(ScPainter::Solid);
p->drawRect(basX + 10, basY + 20, 230, 220);
p->setFillMode(ScPainter::None);
p->drawRect(basX + 230, basY + 5, 11, 11);
p->setPen(QColor(0, 0, 0), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
p->drawLine(QPointF(basX + 232, basY + 13), QPointF(basX + 239, basY + 13));
p->save();
p->translate(basX + 10, basY + 5);
p->scale(0.5, 0.5);
drawNoteIcon(p);
p->restore();
clp = QPainterPath();
clp.addRect(basX + 10, basY + 20, 230, 220);
clpArr.fromQPainterPath(clp);
p->setupPolygon(&clpArr);
p->setClipPath();
p->setPen(fontColor);
p->setFont(QFont(fontName, fontSize));
p->drawText(QRectF(basX + 11, basY + 21, 228, 218), bmUtf16, false, 2);
p->restore();
}
else
drawNoteIcon(p);
p->restore();
p->restore();
return;
}
}
p->restore();
}
/* Experimental Addition to display an Image as Background */
/*
else if ((!Pfile.isEmpty()) && (PictureIsAvailable) && (PicArt))
{
p->save();
if (imageClip.size() != 0)
{
p->setupPolygon(&imageClip);
p->setClipPath();
}
p->setupPolygon(&PoLine);
p->setClipPath();
if (imageFlippedH())
{
p->translate(Width, 0);
p->scale(-1, 1);
}
if (imageFlippedV())
{
p->translate(0, Height);
p->scale(1, -1);
}
p->translate(LocalX*LocalScX, LocalY*LocalScY);
p->scale(LocalScX, LocalScY);
if (pixm.imgInfo.lowResType != 0)
p->scale(pixm.imgInfo.lowResScale, pixm.imgInfo.lowResScale);
p->drawImage(pixm.qImagePtr());
p->restore();
}
*/
if (itemText.length() != 0)
{
// qDebug("drawing textframe: len=%d", itemText.length());
if (imageFlippedH())
{
p->translate(m_width, 0);
p->scale(-1, 1);
pf2.translate(m_width, 0);
pf2.scale(-1, 1);
}
if (imageFlippedV())
{
p->translate(0, m_height);
p->scale(1, -1);
pf2.translate(0, m_height);
pf2.scale(1, -1);
}
assert( firstInFrame() >= 0 );
assert( lastInFrame() < itemText.length() );
int fm = p->fillMode();
int sm = p->strokeMode();
p->setFillMode(1);
p->setStrokeMode(1);
ScreenPainter painter(p, this);
textLayout.renderBackground(&painter);
textLayout.render(&painter, this);
p->setFillMode(fm);
p->setStrokeMode(sm);
}
m_textDistanceMargins=savedTextDistanceMargins;
p->restore();//RE1
}
void PageItem_TextFrame::DrawObj_Post(ScPainter *p)
{
if (m_Doc->layerOutline(LayerID))
{
p->setPen(m_Doc->layerMarker(LayerID), 1, Qt::