Subversion Repositories Scribus

Rev

Rev 20307 | Rev 21146 | 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_pathtext.h"
#include <QGridLayout>
#include <QImage>
#include <QLabel>
#include <QList>
#include <QPainterPath>

#include <cassert>

#include "scconfig.h"

#include "commonstrings.h"
#include "pageitem.h"
#include "prefsmanager.h"
#include "scpage.h"
#include "scpainter.h"
#include "scpaths.h"
#include "scraction.h"

#include "scribusstructs.h"
#include "scribusdoc.h"
#include "undomanager.h"
#include "undostate.h"
#include "util.h"
#include "util_math.h"
#include "text/boxes.h"
#include "text/textlayoutpainter.h"
#include "text/screenpainter.h"

using namespace std;

PageItem_PathText::PageItem_PathText(ScribusDoc *pa, double x, double y, double w, double h, double w2, QString fill, QString outline)
        : PageItem(pa, PageItem::PathText, x, y, w, h, w2, fill, outline), itemRenderText(pa)
{
        firstChar = 0;
        MaxChars = itemText.length();
}


void PageItem_PathText::layout()
{
        QImage pgPix(10, 10, QImage::Format_ARGB32_Premultiplied);
        QRectF rd; // = QRect(0,0,9,9);
        ScPainter *painter = new ScPainter(&pgPix, pgPix.width(), pgPix.height());     
        DrawObj(painter, rd);
        painter->end();
        delete painter;
        updatePolyClip();
}


void PageItem_PathText::DrawObj_Item(ScPainter *p, QRectF cullingArea)
{
        itemText.invalidateAll();
        firstChar = 0;
        MaxChars = 0;
        int a;
        QString chstr, chstr2, chstr3;
        double dx;
        FPoint point = FPoint(0, 0);
        FPoint tangent = FPoint(0, 0);
        CurX = m_textDistanceMargins.left();

        if (!m_Doc->layerOutline(LayerID))
        {
                if (PoShow)
                {
                        p->setupPolygon(&PoLine, false);
                        if (NamedLStyle.isEmpty())
                        {
                                if ((!patternStrokeVal.isEmpty()) && (m_Doc->docPatterns.contains(patternStrokeVal)))
                                {
                                        if (patternStrokePath)
                                        {
                                                QPainterPath guidePath = PoLine.toQPainterPath(false);
                                                DrawStrokePattern(p, guidePath);
                                        }
                                        else
                                        {
                                                p->setPattern(&m_Doc->docPatterns[patternStrokeVal], patternStrokeScaleX, patternStrokeScaleY, patternStrokeOffsetX, patternStrokeOffsetY, patternStrokeRotation, patternStrokeSkewX, patternStrokeSkewY, patternStrokeMirrorX, patternStrokeMirrorY);
                                                p->setStrokeMode(ScPainter::Pattern);
                                                p->strokePath();
                                        }
                                }
                                else if (GrTypeStroke > 0)
                                {
                                        if ((!gradientStrokeVal.isEmpty()) && (!m_Doc->docGradients.contains(gradientStrokeVal)))
                                                gradientStrokeVal = "";
                                        if (!(gradientStrokeVal.isEmpty()) && (m_Doc->docGradients.contains(gradientStrokeVal)))
                                                stroke_gradient = m_Doc->docGradients[gradientStrokeVal];
                                        if (stroke_gradient.Stops() < 2) // fall back to solid stroking if there are not enough colorstops in the gradient.
                                        {
                                                if (lineColor() != CommonStrings::None)
                                                {
                                                        p->setBrush(strokeQColor);
                                                        p->setStrokeMode(ScPainter::Solid);
                                                }
                                                else
                                                        p->setStrokeMode(ScPainter::None);
                                        }
                                        else
                                        {
                                                p->setStrokeMode(ScPainter::Gradient);
                                                p->stroke_gradient = stroke_gradient;
                                                if (GrTypeStroke == 6)
                                                        p->setGradient(VGradient::linear, FPoint(GrStrokeStartX, GrStrokeStartY), FPoint(GrStrokeEndX, GrStrokeEndY), FPoint(GrStrokeStartX, GrStrokeStartY), GrStrokeScale, GrStrokeSkew);
                                                else
                                                        p->setGradient(VGradient::radial, FPoint(GrStrokeStartX, GrStrokeStartY), FPoint(GrStrokeEndX, GrStrokeEndY), FPoint(GrStrokeFocalX, GrStrokeFocalY), GrStrokeScale, GrStrokeSkew);
                                        }
                                        p->strokePath();
                                }
                                else if (lineColor() != CommonStrings::None)
                                {
                                        p->setStrokeMode(ScPainter::Solid);
                                        p->strokePath();
                                }
                        }
                        else
                        {
                                p->setStrokeMode(ScPainter::Solid);
                                multiLine ml = m_Doc->MLineStyles[NamedLStyle];
                                QColor tmp;
                                for (int it = ml.size()-1; it > -1; it--)
                                {
                                        if (ml[it].Color != CommonStrings::None) // && (ml[it].Width != 0))
                                        {
                                                SetQColor(&tmp, ml[it].Color, ml[it].Shade);
                                                p->setPen(tmp, ml[it].Width, static_cast<Qt::PenStyle>(ml[it].Dash), static_cast<Qt::PenCapStyle>(ml[it].LineEnd), static_cast<Qt::PenJoinStyle>(ml[it].LineJoin));
                                                p->strokePath();
                                        }
                                }
                        }
                }
        }
        double totalTextLen = 0.0;
        double totalCurveLen = 0.0;
        double extraOffset = 0.0;
        if (itemText.length() != 0)
        {
                CurX += itemText.charStyle(0).fontSize() * itemText.charStyle(0).tracking() / 10000.0;
                totalTextLen += itemText.charStyle(0).fontSize() * itemText.charStyle(0).tracking() / 10000.0;
        }
        itemRenderText.clear();
        itemRenderText.setDoc(m_Doc);
        itemRenderText.setDefaultStyle(itemText.defaultStyle());
        for (a = firstChar; a < itemText.length(); ++a)
        {
                CharStyle nstyle = itemText.charStyle(a);
                ParagraphStyle pstyle = itemText.paragraphStyle(a);
                chstr = itemText.text(a, 1);
                if ((chstr[0] == SpecialChars::OBJECT) && (itemText.hasObject(a)))
                {
                        int pot = itemRenderText.length();
                        itemRenderText.insertObject(pot, itemText.object(a)->inlineCharID);
                }
                else
                {
                        if (!(chstr[0] == SpecialChars::PARSEP || chstr[0] == SpecialChars::TAB || chstr[0] == SpecialChars::LINEBREAK))
                                chstr = ExpandToken(a);
                        for (int cc = 0; cc < chstr.count(); cc++)
                        {
                                int pot = itemRenderText.length();
                                itemRenderText.insertChars(pot, chstr.mid(cc, 1));
                                itemRenderText.applyStyle(pot, pstyle);
                                itemRenderText.applyCharStyle(pot, 1, nstyle);
                        }
                }
                chstr.clear();
        }
        textLayout.setStory(&itemRenderText);
        int spaceCount = 0;
        double wordExtra = 0;
        for (a = firstChar; a < itemRenderText.length(); ++a)
        {
                chstr = itemRenderText.text(a, 1);
                if (chstr[0] == SpecialChars::PAGENUMBER || chstr[0] == SpecialChars::PARSEP || chstr[0] == SpecialChars::PAGECOUNT
                        || chstr[0] == SpecialChars::TAB || chstr[0] == SpecialChars::LINEBREAK)
                        continue;
                if (chstr[0] == SpecialChars::BLANK)
                        spaceCount++;
                if (a < itemRenderText.length()-1)
                        chstr += itemRenderText.text(a+1, 1);
                GlyphLayout glyphs = layoutGlyphs(itemRenderText.charStyle(a), chstr, itemRenderText.flags(a));
                if (itemRenderText.hasObject(a))
                        totalTextLen += (itemRenderText.object(a)->width() + itemRenderText.object(a)->lineWidth()) * glyphs.scaleH;
                else
                        totalTextLen += glyphs.xadvance+itemRenderText.charStyle(a).fontSize() * itemRenderText.charStyle(a).tracking() / 10000.0;
        }
        for (int segs = 0; segs < PoLine.size()-3; segs += 4)
        {
                totalCurveLen += PoLine.lenPathSeg(segs);
        }
        if ((itemRenderText.paragraphStyle(0).alignment() != 0) && (totalCurveLen >= totalTextLen + m_textDistanceMargins.left()))
        {
                if (itemRenderText.paragraphStyle(0).alignment() == 2)
                {
                        CurX = totalCurveLen  - totalTextLen;
                        CurX -= m_textDistanceMargins.left();
                }
                if (itemRenderText.paragraphStyle(0).alignment() == 1)
                        CurX = (totalCurveLen - totalTextLen) / 2.0;
                if (itemRenderText.paragraphStyle(0).alignment() == 3)
                {
                        if (spaceCount != 0)
                        {
                                extraOffset = 0;
                                wordExtra = (totalCurveLen - m_textDistanceMargins.left()  - totalTextLen) / static_cast<double>(spaceCount);
                        }
                        else
                        {
                                extraOffset = (totalCurveLen - m_textDistanceMargins.left()  - totalTextLen) / static_cast<double>(itemRenderText.length());
                                wordExtra = 0;
                        }
                }
                if (itemRenderText.paragraphStyle(0).alignment() == 4)
                        extraOffset = (totalCurveLen - m_textDistanceMargins.left() - totalTextLen) / static_cast<double>(itemRenderText.length());
        }
        QPainterPath guidePath = PoLine.toQPainterPath(false);
        QList<QPainterPath> pathList = decomposePath(guidePath);
        QPainterPath currPath = pathList[0];
        int currPathIndex = 0;
        PathLineBox* linebox = new PathLineBox();
        for (a = firstChar; a < itemRenderText.length(); ++a)
        {
                CurY = 0;
                PathData* pdata = & (textLayout.point(a));
                chstr = itemRenderText.text(a,1);
                if (chstr[0] == SpecialChars::PAGENUMBER || chstr[0] == SpecialChars::PARSEP || chstr[0] == SpecialChars::PAGECOUNT
                        || chstr[0] == SpecialChars::TAB || chstr[0] == SpecialChars::LINEBREAK)
                        continue;
                if (a < itemRenderText.length()-1)
                        chstr += itemRenderText.text(a+1, 1);

                GlyphLayout glyphs = layoutGlyphs(itemRenderText.charStyle(a), chstr, itemRenderText.flags(a));
                GlyphRun run(&itemRenderText.charStyle(a), itemRenderText.flags(a), a, a, itemRenderText.object(a));
                run.glyphs().append(glyphs);

                if (itemRenderText.hasObject(a))
                        dx = (itemRenderText.object(a)->width() + itemRenderText.object(a)->lineWidth()) * glyphs.scaleH / 2.0;
                else
                        dx = glyphs.xadvance / 2.0;
                CurX += dx;
                double currPerc = currPath.percentAtLength(CurX);
                if (currPerc >= 0.9999999)
                {
                        currPathIndex++;
                        if (currPathIndex == pathList.count())
                        {
                                MaxChars = a;
                                break;
                        }
                        currPath = pathList[currPathIndex];
                        CurX = dx;
                        currPerc = currPath.percentAtLength(CurX);
                }
                double currAngle = currPath.angleAtPercent(currPerc);
                if (currAngle <= 180.0)
                        currAngle *= -1.0;
                else
                        currAngle = 360.0 - currAngle;
                QPointF currPoint = currPath.pointAtPercent(currPerc);
                tangent = FPoint(cos(currAngle * M_PI / 180.0), sin(currAngle * M_PI / 180.0));
                point = FPoint(currPoint.x(), currPoint.y());
                glyphs.xoffset = 0;
                pdata->PtransX = point.x();
                pdata->PtransY = point.y();
                pdata->PRot    = currAngle * M_PI / 180.0;
                pdata->PDx     = dx;
                QTransform trafo = QTransform( 1, 0, 0, -1, -dx, 0 );
                if (textPathFlipped)
                        trafo *= QTransform(1, 0, 0, -1, 0, 0);
                if (textPathType == 0)
                        trafo *= QTransform( tangent.x(), tangent.y(), tangent.y(), -tangent.x(), point.x(), point.y() ); // ID's Rainbow mode
                else if (textPathType == 1)
                        trafo *= QTransform( 1, 0, 0, -1, point.x(), point.y() ); // ID's Stair Step mode
                else if (textPathType == 2)
                {
                        double a = 1;
                        if (tangent.x() < 0)
                                a = -1;
                        if (fabs(tangent.x()) > 0.1)
                                trafo *= QTransform( a, (tangent.y() / tangent.x()) * a, 0, -1, point.x(), point.y() ); // ID's Skew mode
                        else
                                trafo *= QTransform( a, 4 * a, 0, -1, point.x(), point.y() );
                }


                const CharStyle& cStyle(run.style());
                double scaleV = cStyle.scaleV() / 1000.0;
                double offset = (cStyle.fontSize() / 10) * (cStyle.baselineOffset() / 1000.0);
                double ascent = cStyle.font().ascent(cStyle.fontSize()/10.00) * scaleV + offset;
                double descent = cStyle.font().descent(cStyle.fontSize()/10.00) * scaleV - offset;
                linebox->setAscent(ascent);
                linebox->setDescent(descent);
                Box* box;
                if (itemRenderText.hasObject(a))
                {
                        box = new ObjectBox(run);
                        box->setAscent(run.object()->height() - run.object()->lineWidth());
                        box->setDescent(0);
                }
                else
                {
                        box = new GlyphBox(run);
                        box->setAscent(linebox->ascent());
                        box->setDescent(linebox->descent());
                }

                box->setMatrix(trafo);
                linebox->addBox(box);


                MaxChars = a+1;
                CurX -= dx;
                if (itemRenderText.hasObject(a))
                        CurX += (itemRenderText.object(a)->width() + itemRenderText.object(a)->lineWidth()) * glyphs.scaleH + extraOffset;
                else if (chstr[0] == SpecialChars::BLANK)
                        CurX += glyphs.xadvance+itemRenderText.charStyle(a).fontSize() * itemRenderText.charStyle(a).tracking() / 10000.0 + wordExtra + extraOffset;
                else
                        CurX += glyphs.xadvance+itemRenderText.charStyle(a).fontSize() *itemRenderText.charStyle(a).tracking() / 10000.0 + extraOffset;
        }

        textLayout.addColumn(0, 0);
        textLayout.appendLine(linebox);

        if (!m_Doc->RePos)
        {
                int fm = p->fillMode();
                p->setFillMode(1);
                p->save();
                ScreenPainter painter(p, this);
                textLayout.render(&painter);
                p->setFillMode(fm);
                p->restore();
        }
}

bool PageItem_PathText::createInfoGroup(QFrame *infoGroup, QGridLayout *infoGroupLayout)
{
        int Parag = 0;
        int Words = 0;
        int Chara = 0;
        int ParagN = 0;
        int WordsN = 0;
        int CharaN = 0;
       
        QLabel *infoCT = new QLabel(infoGroup);
        QLabel *linesCT = new QLabel(infoGroup);
        QLabel *linesT = new QLabel(infoGroup);
        QLabel *parCT = new QLabel(infoGroup);
        QLabel *parT = new QLabel(infoGroup);
        QLabel *wordCT = new QLabel(infoGroup);
        QLabel *wordT = new QLabel(infoGroup);
        QLabel *charCT = new QLabel(infoGroup);
        QLabel *charT = new QLabel(infoGroup);
       
       
        infoCT->setText("Text on a Path");
        infoGroupLayout->addWidget( infoCT, 0, 0, 1, 2, Qt::AlignCenter );
       
        WordAndPara(this, &Words, &Parag, &Chara, &WordsN, &ParagN, &CharaN);
        parCT->setText(tr("Paragraphs: "));
        infoGroupLayout->addWidget( parCT, 1, 0, Qt::AlignRight );
        if (ParagN != 0)
                parT->setText(QString::number(Parag+ParagN)+" ("+QString::number(ParagN)+")");
        else
                parT->setText(QString::number(Parag));
        infoGroupLayout->addWidget( parT, 1, 1 );
       
        linesCT->setText(tr("Lines: "));
        infoGroupLayout->addWidget( linesCT, 2, 0, Qt::AlignRight );
        linesT->setText(QString::number(textLayout.lines()));
        infoGroupLayout->addWidget( linesT, 2, 1 );
       
       
        wordCT->setText(tr("Words: "));
        infoGroupLayout->addWidget( wordCT, 3, 0, Qt::AlignRight );
        if (WordsN != 0)
                wordT->setText(QString::number(Words+WordsN)+" ("+QString::number(WordsN)+")");
        else
                wordT->setText(QString::number(Words));
        infoGroupLayout->addWidget( wordT, 3, 1 );
       
        charCT->setText(tr("Chars: "));
        infoGroupLayout->addWidget(charCT, 4, 0, Qt::AlignRight );
        if (CharaN != 0)
                charT->setText(QString::number(Chara+CharaN)+" ("+QString::number(CharaN)+")");
        else
                charT->setText(QString::number(Chara));
        infoGroupLayout->addWidget( charT, 4, 1 );
        return true;
}

void PageItem_PathText::applicableActions(QStringList & actionList)
{
        actionList << "toolsEditWithStoryEditor";
        actionList << "itemConvertToOutlines";
}

QString PageItem_PathText::infoDescription()
{
        return QString();
}

void PageItem_PathText::getVisualBoundingRect(double * x1, double * y1, double * x2, double * y2) const
{
        PageItem::getVisualBoundingRect(x1, y1, x2, y2);
        QRectF totalRect(QPointF(*x1, *y1), QPointF(*x2, *y2));
        QTransform clipTrans;
        clipTrans.translate(m_xPos, m_yPos);
        clipTrans.rotate(m_rotation);
        totalRect = totalRect.united(QRectF(clipTrans.mapRect(Clip.boundingRect())));
        totalRect.getCoords(x1, y1, x2, y2);
}