mirror of
https://github.com/jkriege2/JKQtPlotter.git
synced 2024-12-26 10:31:39 +08:00
69ad2a0182
- improved documentation - changed: using static const variables instead of \c #define for fixed default values (e.g. JKQTPImageTools::LUTSIZE, JKQTPImageTools::PALETTE_ICON_WIDTH, JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, JKQTMathText::ABS_MIN_LINEWIDTH ...) - new: added debugging option, which surrounds different regions with visible rectangles (JKQTBasePlotter::enableDebugShowRegionBoxes() ) - fixed: colorbars at top were positioned over the plot label - new: frames (plot viewport, key/legend ...) may be rounded off at the corners - new: diverse new styling options (default font name/size ...) - speed improvements to JKQTMathText::useSTIX()
1122 lines
25 KiB
C++
1122 lines
25 KiB
C++
/***************************************************************************
|
|
File : QTeXPaintEngine.cpp
|
|
Project : QTeXEngine GNU GPL v. 3.0
|
|
--------------------------------------------------------------------
|
|
Copyright : (C) 2009 by Ion Vasilief
|
|
Email (use @ for *) : ion_vasilief*yahoo.fr
|
|
Description : Enables the export of QPainter grafics to .tex files
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* 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.1 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the Free Software *
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, *
|
|
* Boston, MA 02110-1301 USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "QTeXEngine.h"
|
|
|
|
#include <math.h>
|
|
#include <QDir>
|
|
#include <QTextStream>
|
|
#include <QDateTime>
|
|
|
|
QTeXPaintEngine::QTeXPaintEngine(const QString& f, QTeXPaintDevice::Unit u)
|
|
: QPaintEngine(QPaintEngine::AllFeatures),
|
|
fname(f),
|
|
d_pixmap_index(1),
|
|
d_unit(u)
|
|
{
|
|
d_pgf_mode = false;
|
|
d_open_scope = false;
|
|
d_gray_scale = false;
|
|
d_document_mode = false;
|
|
d_escape_text = true;
|
|
d_font_size = true;
|
|
d_clip_path = QPainterPath();
|
|
d_current_color = QColor();
|
|
d_pattern_color = QColor();
|
|
d_horizontal_alignment = Qt::AlignHCenter;
|
|
}
|
|
|
|
bool QTeXPaintEngine::begin(QPaintDevice* p)
|
|
{
|
|
setPaintDevice(p);
|
|
file = new QFile(fname);
|
|
if (file->open(QIODevice::WriteOnly)){
|
|
QTextStream t(file);
|
|
t.setCodec("UTF-8");
|
|
if (d_document_mode){
|
|
t << "\\documentclass{article}\n";
|
|
t << "\\usepackage[left=0.2cm,top=0.1cm,right=0.2cm,nohead,nofoot]{geometry}\n";
|
|
|
|
if (d_pgf_mode){
|
|
t << "\\usepackage{pgf}\n";
|
|
t << "\\usepgflibrary{patterns}\n";
|
|
} else {
|
|
t << "\\usepackage{tikz}\n";
|
|
t << "\\usetikzlibrary{patterns}\n";
|
|
}
|
|
|
|
t << "\\usepackage{ulem}\n";//used for striked out fonts (\sout command)
|
|
t << "\\begin{document}\n";
|
|
}
|
|
|
|
QString pictureEnv = "\\begin{tikzpicture}{0";
|
|
if (d_pgf_mode)
|
|
pictureEnv = "\\begin{pgfpicture}{0";
|
|
|
|
QString u = unit();
|
|
t << pictureEnv + u + "}{0" + u + "}{";
|
|
t << QString::number(p->width()) + u + "}{";
|
|
t << QString::number(p->height()) + u + "}\n";
|
|
|
|
if (!d_pgf_mode){
|
|
QPainterPath path;
|
|
path.addRect(QRect(0, 0, p->width(), p->height()));
|
|
t << "\t\\clip" + tikzPath(path);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
delete file;
|
|
return false;
|
|
}
|
|
|
|
bool QTeXPaintEngine::end()
|
|
{
|
|
QTextStream t(file);
|
|
t.setCodec("UTF-8");
|
|
if (d_open_scope)
|
|
t << endScope();
|
|
|
|
if (d_pgf_mode)
|
|
t << "\\end{pgfpicture}\n";
|
|
else
|
|
t << "\\end{tikzpicture}\n";
|
|
|
|
if (d_document_mode)
|
|
t << "\\end{document}\n";
|
|
|
|
file->close();
|
|
return true;
|
|
}
|
|
|
|
void QTeXPaintEngine::drawPoints ( const QPointF * points, int pointCount )
|
|
{
|
|
if (emptyStringOperation())
|
|
return;
|
|
|
|
QMatrix m = painter()->worldMatrix();
|
|
double lw = painter()->pen().widthF();
|
|
|
|
QString s = QString::null;
|
|
if (addNewPenColor()){
|
|
d_current_color = painter()->pen().color();
|
|
s = color(d_current_color);
|
|
}
|
|
|
|
for (int i = 0; i < pointCount; i++){
|
|
QPointF p = m.map(points[i]);
|
|
if (d_pgf_mode){
|
|
QString path = pgfPoint(convertPoint(p));
|
|
path += pgfPoint(QPointF(lw, lw)) + "\n";
|
|
s += "\\pgfrect[fill]" + path;
|
|
} else {
|
|
QString path = tikzPoint(convertPoint(p));
|
|
path += " rectangle " + tikzPoint(convertPoint(QPointF(p.x() + lw, p.y() + lw))) + ";\n";
|
|
s += "\\fill " + path;
|
|
}
|
|
}
|
|
|
|
writeToFile(s);
|
|
}
|
|
|
|
void QTeXPaintEngine::drawLines ( const QLineF * lines, int lineCount )
|
|
{
|
|
if (painter()->pen().style() == Qt::NoPen)
|
|
return;
|
|
|
|
QString s;
|
|
for (int i = 0; i < lineCount; i++){
|
|
QPainterPath path(lines[i].p1());
|
|
path.lineTo(lines[i].p2());
|
|
|
|
s += drawShape(Line, this->path(path));
|
|
}
|
|
|
|
writeToFile(s);
|
|
}
|
|
|
|
void QTeXPaintEngine::drawPolygon ( const QPointF * points, int pointCount, PolygonDrawMode mode )
|
|
{
|
|
if (emptyStringOperation())
|
|
return;
|
|
|
|
QVector<QPointF> pts;
|
|
for (int i = 0; i < pointCount; i++)
|
|
pts << points[i];
|
|
|
|
QPainterPath path;
|
|
path.addPolygon(QPolygonF(pts));
|
|
if (mode != QPaintEngine::PolylineMode)
|
|
path.closeSubpath ();
|
|
|
|
QString s;;
|
|
if (mode != QPaintEngine::PolylineMode){
|
|
path.closeSubpath ();
|
|
s += drawShape(Polygon, this->path(path));
|
|
} else
|
|
s += drawShape(Polyline, this->path(path));
|
|
|
|
writeToFile(s);
|
|
}
|
|
|
|
void QTeXPaintEngine::drawTextItem ( const QPointF & p, const QTextItem & textItem )
|
|
{
|
|
QString s = QString::null;
|
|
if (addNewPenColor()){
|
|
s = color(painter()->pen().color());
|
|
d_current_color = painter()->pen().color();
|
|
}
|
|
|
|
QMatrix m = painter()->worldMatrix();
|
|
|
|
s += "\\pgftext[";
|
|
|
|
QPointF origin = p;
|
|
switch(d_horizontal_alignment){
|
|
case Qt::AlignLeft:
|
|
s += "left";
|
|
break;
|
|
|
|
case Qt::AlignHCenter:
|
|
case Qt::AlignJustify:
|
|
origin = QPointF(p.x() + 0.5*textItem.width(), p.y());
|
|
s += "center";
|
|
break;
|
|
|
|
case Qt::AlignRight:
|
|
origin = QPointF(p.x() + textItem.width(), p.y());
|
|
s += "right";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
s += ", base, at=";
|
|
s += pgfPoint(convertPoint(m.map(origin)));
|
|
|
|
if (painter()->transform().isRotating ()){
|
|
double angle = 180.0/M_PI*acos(m.m11());
|
|
if (m.m11() != 0.0 && m.m12() > 0)
|
|
angle = -angle;
|
|
s += ",rotate=" + QString::number(angle);
|
|
}
|
|
s += "]{";
|
|
|
|
QFont f = textItem.font();
|
|
if (d_font_size)
|
|
s += "\\fontsize{" + QString::number(int(f.pointSizeF())) + "}{0}\\selectfont{";
|
|
|
|
if (f.underline())
|
|
s += "\\underline{";
|
|
if (f.italic())
|
|
s += "\\textit{";
|
|
if (f.bold())
|
|
s += "\\textbf{";
|
|
if (f.strikeOut())
|
|
s += "\\sout{";
|
|
|
|
QString text = textItem.text();
|
|
text.remove(QRegExp("~\\"));
|
|
if (d_escape_text){
|
|
text.replace("$", "\\$");
|
|
text.replace("_", "\\_");
|
|
text.replace("{", "\\{");
|
|
text.replace("}", "\\}");
|
|
text.replace("^", "\\^");
|
|
text.replace("&", "\\&");
|
|
text.replace("%", "\\%");
|
|
text.replace("#", "\\#");
|
|
}
|
|
|
|
s += text;
|
|
if (d_font_size)
|
|
s += "}";
|
|
|
|
if (f.italic())
|
|
s += "}";
|
|
if (f.bold())
|
|
s += "}";
|
|
if (f.underline())
|
|
s += "}";
|
|
if (f.strikeOut())
|
|
s += "}";
|
|
|
|
s += "}\n";
|
|
|
|
writeToFile(s);
|
|
}
|
|
|
|
void QTeXPaintEngine::drawRects ( const QRectF * rects, int rectCount )
|
|
{
|
|
if (emptyStringOperation())
|
|
return;
|
|
|
|
QString s;
|
|
for (int i = 0; i < rectCount; i++){
|
|
QPainterPath path;
|
|
path.addPolygon(QPolygonF(rects[i]));
|
|
s += drawShape(Path, this->path(path));
|
|
}
|
|
|
|
writeToFile(s);
|
|
}
|
|
|
|
void QTeXPaintEngine::drawEllipse ( const QRectF & rect )
|
|
{
|
|
if (emptyStringOperation())
|
|
return;
|
|
|
|
QPointF p = painter()->worldMatrix().map(rect.bottomLeft());
|
|
|
|
QString path;
|
|
if (d_pgf_mode){
|
|
path = pgfPoint(convertPoint(QPointF(p.x() + 0.5*rect.width(), p.y() - 0.5*rect.height())));
|
|
path += pgfPoint(QPointF(0, 0.5*rect.height()*resFactorY()));
|
|
path += pgfPoint(QPointF(0.5*rect.width()*resFactorX(), 0)) + "\n";
|
|
} else {
|
|
path = tikzPoint(convertPoint(QPointF(p.x() + 0.5*rect.width(), p.y() - 0.5*rect.height())));
|
|
path += " ellipse (";
|
|
QString u = unit();
|
|
path += QString::number(0.5*rect.width()*resFactorX()) + u + " and ";
|
|
path += QString::number(0.5*rect.height()*resFactorY()) + u + ");\n";
|
|
}
|
|
|
|
writeToFile(drawShape(Ellipse, path));
|
|
}
|
|
|
|
void QTeXPaintEngine::drawPath ( const QPainterPath & path )
|
|
{
|
|
if (emptyStringOperation())
|
|
return;
|
|
|
|
writeToFile(drawShape(Path, this->path(path)));
|
|
}
|
|
|
|
QString QTeXPaintEngine::drawPgfShape(Shape shape, const QString & path)
|
|
{
|
|
if (path.isEmpty())
|
|
return QString::null;
|
|
|
|
QString stroke_command = path + "\\pgfstroke\n";
|
|
QString fill_command = path + "\\pgffill\n";
|
|
switch(shape){
|
|
case Line:
|
|
case Polygon:
|
|
case Polyline:
|
|
case Path:
|
|
case Points:
|
|
break;
|
|
|
|
case Rect:
|
|
stroke_command = "\\pgfrect[stroke]" + path;
|
|
fill_command = "\\pgfrect[fill]" + path;
|
|
break;
|
|
|
|
case Ellipse:
|
|
stroke_command = "\\pgfellipse[stroke]" + path;
|
|
fill_command = "\\pgfellipse[fill]" + path;
|
|
break;
|
|
}
|
|
|
|
QString s = QString::null;
|
|
if (shape != Line && shape != Polyline && painter()->brush().style() != Qt::NoBrush){
|
|
// fill the background
|
|
s += pgfBrush(painter()->brush());
|
|
s += fill_command;
|
|
}
|
|
|
|
if (painter()->pen().style() != Qt::NoPen){// draw the contour
|
|
s += pgfPen(painter()->pen());
|
|
s += stroke_command;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
QString QTeXPaintEngine::drawShape(Shape shape, const QString & path)
|
|
{
|
|
if (d_pgf_mode)
|
|
return drawPgfShape(shape, path);
|
|
|
|
return drawTikzShape(shape, path);
|
|
}
|
|
|
|
QString QTeXPaintEngine::drawTikzShape(Shape shape, const QString & path)
|
|
{
|
|
QString s = QString::null;
|
|
if (path.isEmpty())
|
|
return s;
|
|
|
|
if (shape != Line && shape != Polyline && painter()->brush().style() != Qt::NoBrush)
|
|
// fill the background
|
|
s += tikzBrush(painter()->brush()) + path;
|
|
if (painter()->pen().style() != Qt::NoPen)// draw the contour
|
|
s += tikzPen(painter()->pen()) + path;
|
|
|
|
return s;
|
|
}
|
|
|
|
void QTeXPaintEngine::drawImage(const QRectF & r, const QImage & image, const QRectF & sr, Qt::ImageConversionFlags flags)
|
|
{
|
|
drawPixmap(QPixmap::fromImage(image, flags).copy(sr.toAlignedRect()), r);
|
|
}
|
|
|
|
void QTeXPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
|
|
{
|
|
drawPixmap(pm.copy(sr.toAlignedRect()), r);
|
|
}
|
|
|
|
void QTeXPaintEngine::drawPixmap(const QPixmap &pix, const QRectF &r)
|
|
{
|
|
QFileInfo fi(*file);
|
|
|
|
QString base = fi.baseName() + "-images-";
|
|
base += QDateTime::currentDateTime().toString("ddMMyy-hhmmss");
|
|
if (!fi.dir().exists(base)){
|
|
if (!fi.dir().mkdir(base))
|
|
return;
|
|
}
|
|
|
|
QString name = fi.dir().absolutePath();
|
|
name += "/" + base + "/image" + QString::number(d_pixmap_index);
|
|
name = QDir::cleanPath(name);
|
|
if (!pix.save(name + ".png", "PNG"))
|
|
return;
|
|
|
|
d_pixmap_index++;
|
|
|
|
QTextStream t(file);
|
|
t.setCodec("UTF-8");
|
|
|
|
t << "\\pgfputat";
|
|
t << pgfPoint(convertPoint(painter()->worldMatrix().map(r.bottomLeft())));
|
|
t << "{\\pgfimage[interpolate=false,width=";
|
|
|
|
QString u = unit();
|
|
t << QString::number(r.width()*resFactorX()) + u + ",height=";
|
|
t << QString::number(r.height()*resFactorY()) + u + "]{";
|
|
t << name;
|
|
t << "}}\n";
|
|
}
|
|
|
|
QString QTeXPaintEngine::clipPath()
|
|
{
|
|
if (painter()->hasClipping()){
|
|
QPainterPath path = painter()->clipPath().simplified();
|
|
if (path.elementCount() > 1000)//latex has a limited main memory size
|
|
return QString::null;
|
|
|
|
if (d_pgf_mode)
|
|
return pgfPath(path) + "\\pgfclip\n";
|
|
else
|
|
return "\\clip" + tikzPath(path);
|
|
}
|
|
return QString::null;
|
|
}
|
|
|
|
QString QTeXPaintEngine::defineColor(const QColor& c, const QString& name)
|
|
{
|
|
QString col = "\\definecolor{" + name + "}{";
|
|
if (d_gray_scale){
|
|
col += "gray}{";
|
|
double gray = qGray(c.rgb())/255.0;
|
|
col += QString::number(gray) + "}\n";
|
|
} else {
|
|
col += "rgb}{";
|
|
col += QString::number(c.redF()) + ",";
|
|
col += QString::number(c.greenF()) + ",";
|
|
col += QString::number(c.blueF()) + "}\n";
|
|
}
|
|
return col;
|
|
}
|
|
|
|
QString QTeXPaintEngine::tikzBrush(const QBrush& brush)
|
|
{
|
|
QString options = QString::null;
|
|
if (addNewPatternColor()){
|
|
options = defineColor(brush.color(), "c");
|
|
d_pattern_color = brush.color();
|
|
}
|
|
|
|
options += "\\fill [pattern color=c, pattern=";
|
|
|
|
switch(brush.style()){
|
|
case Qt::NoBrush:
|
|
return QString::null;
|
|
break;
|
|
|
|
case Qt::SolidPattern:
|
|
{
|
|
QString s = QString::null;
|
|
if (addNewBrushColor()){
|
|
d_current_color = brush.color();
|
|
s = color(brush.color());
|
|
}
|
|
|
|
s += "\\fill";
|
|
|
|
double alpha = painter()->brush().color().alphaF();
|
|
if(alpha > 0.0 && alpha < 1.0)
|
|
s += "[opacity=" + QString::number(alpha) + "]";
|
|
|
|
return s;
|
|
}
|
|
break;
|
|
|
|
case Qt::Dense1Pattern:
|
|
case Qt::Dense2Pattern:
|
|
case Qt::Dense3Pattern:
|
|
case Qt::Dense4Pattern:
|
|
options += "crosshatch dots";
|
|
break;
|
|
|
|
case Qt::Dense5Pattern:
|
|
case Qt::Dense6Pattern:
|
|
case Qt::Dense7Pattern:
|
|
options += "dots";
|
|
break;
|
|
|
|
case Qt::HorPattern:
|
|
options += "horizontal lines";
|
|
break;
|
|
case Qt::VerPattern:
|
|
options += "vertical lines";
|
|
break;
|
|
case Qt::CrossPattern:
|
|
options += "grid";
|
|
break;
|
|
case Qt::BDiagPattern:
|
|
options += "north east lines";
|
|
break;
|
|
case Qt::FDiagPattern:
|
|
options += "north west lines";
|
|
break;
|
|
case Qt::DiagCrossPattern:
|
|
options += "crosshatch";
|
|
break;
|
|
|
|
case Qt::LinearGradientPattern:
|
|
{
|
|
const QLinearGradient *qtgradient = (const QLinearGradient *)brush.gradient();
|
|
QGradientStops stops = qtgradient->stops();
|
|
|
|
QString lc = defineColor(stops.first().second, "lc");
|
|
QString rc = defineColor(stops.last().second, "rc");
|
|
|
|
QMatrix m = painter()->worldMatrix();
|
|
QPointF sp = m.map(qtgradient->start());
|
|
QPointF ep = m.map(qtgradient->finalStop());
|
|
|
|
options = lc + rc + "\\fill [";
|
|
options += "left color=lc, ";
|
|
options += "right color=rc, ";
|
|
options += "shading angle=" + QString::number(-QLineF(sp, ep).angle());
|
|
}
|
|
break;
|
|
|
|
case Qt::RadialGradientPattern:
|
|
{
|
|
const QRadialGradient *qtgradient = (const QRadialGradient *)brush.gradient();
|
|
QGradientStops stops = qtgradient->stops();
|
|
|
|
QString colors;
|
|
int count = stops.count();
|
|
colors += defineColor(stops[0].second, "c1");
|
|
colors += defineColor(stops[count - 1].second, "c2");
|
|
|
|
/*for (int i = 0; i < count; i++){
|
|
QGradientStop stop = stops[i];
|
|
colors += defineColor(stop.second, "c" + QString::number(i + 1));
|
|
}*/
|
|
|
|
options = colors + "\\fill [";
|
|
options += "inner color=c1, ";
|
|
options += "outer color=c2, ";
|
|
options += "shading=radial";
|
|
qWarning("QTeXEngine: Uncentered Qt::RadialGradientPattern with more than two colors not supported.");
|
|
|
|
}
|
|
break;
|
|
|
|
case Qt::ConicalGradientPattern:
|
|
{
|
|
qWarning("QTeXEngine: Qt::ConicalGradientPattern is not supported.");
|
|
return QString::null;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return options + "]";
|
|
}
|
|
|
|
QString QTeXPaintEngine::pgfBrush(const QBrush& brush)
|
|
{
|
|
QString s = QString::null;
|
|
QColor c = brush.color();
|
|
QString col = defineColor(c, "c");
|
|
QString command = "\\pgfsetfillpattern{";
|
|
switch(brush.style()){
|
|
case Qt::NoBrush:
|
|
break;
|
|
|
|
case Qt::SolidPattern:
|
|
s += color(c);
|
|
break;
|
|
|
|
case Qt::Dense1Pattern:
|
|
case Qt::Dense2Pattern:
|
|
case Qt::Dense3Pattern:
|
|
case Qt::Dense4Pattern:
|
|
s += col + command + "crosshatch dots}{c}\n";
|
|
break;
|
|
|
|
case Qt::Dense5Pattern:
|
|
case Qt::Dense6Pattern:
|
|
case Qt::Dense7Pattern:
|
|
s += col + command + "dots}{c}\n";
|
|
break;
|
|
|
|
case Qt::HorPattern:
|
|
s += col + command + "horizontal lines}{c}\n";
|
|
break;
|
|
case Qt::VerPattern:
|
|
s += col + command + "vertical lines}{c}\n";
|
|
break;
|
|
case Qt::CrossPattern:
|
|
s += col + command + "grid}{c}\n";
|
|
break;
|
|
case Qt::BDiagPattern:
|
|
s += col + command + "north east lines}{c}\n";
|
|
break;
|
|
case Qt::FDiagPattern:
|
|
s += col + command + "north west lines}{c}\n";
|
|
break;
|
|
case Qt::DiagCrossPattern:
|
|
s += col + command + "crosshatch}{c}\n";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
QString QTeXPaintEngine::path(const QPainterPath & path)
|
|
{
|
|
if (path.isEmpty ())
|
|
return QString::null;
|
|
|
|
if (d_pgf_mode)
|
|
return pgfPath(path);
|
|
|
|
return tikzPath(path);
|
|
}
|
|
|
|
QString QTeXPaintEngine::pgfPath(const QPainterPath & path)
|
|
{
|
|
QString s = QString::null;
|
|
int points = path.elementCount();
|
|
QMatrix m = painter()->worldMatrix();
|
|
int curvePoints = 0;
|
|
for (int i = 0; i < points; i++){
|
|
QPainterPath::Element el = path.elementAt(i);
|
|
QPointF p = m.map(QPointF(el.x, el.y));
|
|
|
|
switch(el.type){
|
|
case QPainterPath::MoveToElement:
|
|
s += "\\pgfmoveto" + pgfPoint(convertPoint(p)) + "\n";
|
|
break;
|
|
|
|
case QPainterPath::LineToElement:
|
|
s += "\\pgflineto" + pgfPoint(convertPoint(p)) + "\n";
|
|
break;
|
|
|
|
case QPainterPath::CurveToElement:
|
|
s += "\\pgfcurveto" + pgfPoint(convertPoint(p));
|
|
curvePoints = 0;
|
|
break;
|
|
|
|
case QPainterPath::CurveToDataElement:
|
|
s += pgfPoint(convertPoint(p));
|
|
curvePoints++;
|
|
if (curvePoints == 2)
|
|
s += "\n";
|
|
break;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
QString QTeXPaintEngine::tikzPath(const QPainterPath & path)
|
|
{
|
|
QString s = QString::null;
|
|
if (path.isEmpty())
|
|
return s;
|
|
|
|
int points = path.elementCount();
|
|
QMatrix m = painter()->worldMatrix();
|
|
int curvePoints = 0;
|
|
for (int i = 0; i < points; i++){
|
|
QPainterPath::Element el = path.elementAt(i);
|
|
QPointF p = m.map(QPointF(el.x, el.y));
|
|
switch(el.type){
|
|
case QPainterPath::MoveToElement:
|
|
s += tikzPoint(convertPoint(p));
|
|
break;
|
|
|
|
case QPainterPath::LineToElement:
|
|
s += " -- " + tikzPoint(convertPoint(p));
|
|
break;
|
|
|
|
case QPainterPath::CurveToElement:
|
|
s += " .. controls " + tikzPoint(convertPoint(p));
|
|
curvePoints = 0;
|
|
break;
|
|
|
|
case QPainterPath::CurveToDataElement:
|
|
curvePoints++;
|
|
if (curvePoints == 1)
|
|
s += " and " + tikzPoint(convertPoint(p));
|
|
else if (curvePoints == 2)
|
|
s += " .. " + tikzPoint(convertPoint(p));
|
|
break;
|
|
}
|
|
}
|
|
return s + ";\n";
|
|
}
|
|
|
|
QPointF QTeXPaintEngine::convertPoint( const QPointF& p)
|
|
{
|
|
return QPointF(resFactorX()*p.x(), paintDevice()->height() - resFactorY()*p.y());
|
|
}
|
|
|
|
double QTeXPaintEngine::unitFactor()
|
|
{
|
|
double factor = 1.0;
|
|
switch (d_unit){
|
|
case QTeXPaintDevice::pt:
|
|
factor = 72.27;
|
|
break;
|
|
case QTeXPaintDevice::bp:
|
|
factor = 72;
|
|
break;
|
|
case QTeXPaintDevice::mm:
|
|
factor = 25.4;
|
|
break;
|
|
case QTeXPaintDevice::cm:
|
|
factor = 2.54;
|
|
break;
|
|
case QTeXPaintDevice::in:
|
|
case QTeXPaintDevice::ex:
|
|
case QTeXPaintDevice::em:
|
|
break;
|
|
}
|
|
return factor;
|
|
}
|
|
|
|
double QTeXPaintEngine::resFactorX()
|
|
{
|
|
return unitFactor()/(double)paintDevice()->logicalDpiX();
|
|
}
|
|
|
|
double QTeXPaintEngine::resFactorY()
|
|
{
|
|
return unitFactor()/(double)paintDevice()->logicalDpiY();
|
|
}
|
|
|
|
QString QTeXPaintEngine::pgfPoint( const QPointF& p)
|
|
{
|
|
QString u = unit();
|
|
QString s = "{\\pgfpoint{" + QString::number(p.x());
|
|
s += u + "}{" + QString::number(p.y()) + u + "}}";
|
|
return s;
|
|
}
|
|
|
|
QString QTeXPaintEngine::tikzPoint(const QPointF & p)
|
|
{
|
|
QString u = unit();
|
|
QString s = "(" + QString::number(p.x());
|
|
s += u + "," + QString::number(p.y()) + u + ")";
|
|
return s;
|
|
}
|
|
|
|
QString QTeXPaintEngine::color( const QColor& col)
|
|
{
|
|
QString s = "\\color[";
|
|
if (d_gray_scale){
|
|
s += "gray]{";
|
|
double gray = qGray(col.rgb())/255.0;
|
|
s += QString::number(gray) + "}\n";
|
|
} else {
|
|
s += "rgb]{";
|
|
s += QString::number(col.redF()) + ",";
|
|
s += QString::number(col.greenF()) + ",";
|
|
s += QString::number(col.blueF()) + "}\n";
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
QString QTeXPaintEngine::pgfPen(const QPen& pen)
|
|
{
|
|
QString s = QString::null;
|
|
if (pen.style() == Qt::NoPen)
|
|
return s;
|
|
|
|
s += color(pen.color());
|
|
s += "\\pgfsetlinewidth{" + QString::number(painter()->pen().widthF()) + "pt}\n";
|
|
|
|
QString aux = "\\pgfsetdash{";
|
|
QString term = "}{0cm}\n";
|
|
|
|
double space_length = 0.08*pen.widthF();
|
|
double dot_length = 0.3*space_length;
|
|
double dash_length = 1.5*space_length;
|
|
|
|
QString dash = "{" + QString::number(dash_length) + "cm}";
|
|
QString dot = "{" + QString::number(dot_length) + "cm}";
|
|
QString space = "{" + QString::number(space_length) + "cm}";
|
|
|
|
switch (pen.style()){
|
|
case Qt::SolidLine:
|
|
break;
|
|
case Qt::DashLine:
|
|
s += aux + dash + space + term;
|
|
break;
|
|
case Qt::DotLine:
|
|
s += aux + dot + space + term;
|
|
break;
|
|
case Qt::DashDotLine:
|
|
s += aux + dash + space + dot + space + term;
|
|
break;
|
|
case Qt::DashDotDotLine:
|
|
s += aux + dash + space + dot + space + dot + space + term;
|
|
break;
|
|
|
|
case Qt::CustomDashLine:
|
|
{
|
|
s += aux;
|
|
QVector<qreal> pattern = pen.dashPattern();
|
|
int count = pattern.count();
|
|
QString u = unit();
|
|
for (int i = 0; i < count; i++)
|
|
s += "{" + QString::number(pattern[i]) + u + "}";
|
|
|
|
s += term;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (pen.joinStyle()){
|
|
case Qt::MiterJoin:
|
|
s += "\\pgfsetmiterjoin\n";
|
|
//s += "\\pgfsetmiterlimit{" + QString::number(pen.miterLimit()) + "pt}\n";
|
|
break;
|
|
case Qt::BevelJoin:
|
|
s += "\\pgfsetbeveljoin\n";
|
|
break;
|
|
case Qt::RoundJoin:
|
|
s += "\\pgfsetroundjoin\n";
|
|
break;
|
|
case Qt::SvgMiterJoin:
|
|
s += "\\pgfsetmiterjoin\n";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (pen.capStyle()){
|
|
case Qt::FlatCap:
|
|
s += "\\pgfsetrectcap\n";
|
|
break;
|
|
case Qt::SquareCap:
|
|
s += "\\pgfsetrectcap\n";
|
|
break;
|
|
case Qt::RoundCap:
|
|
s += "\\pgfsetroundcap\n";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
QString QTeXPaintEngine::tikzPen(const QPen& pen)
|
|
{
|
|
if (pen.style() == Qt::NoPen)
|
|
return QString::null;
|
|
|
|
QString col = QString::null;
|
|
if (addNewPenColor()){
|
|
col = color(pen.color());
|
|
d_current_color = pen.color();
|
|
}
|
|
|
|
QString options = "[line width=";
|
|
options += QString::number(painter()->pen().widthF()) + "pt, ";
|
|
|
|
double space_length = 0.08*pen.widthF();
|
|
double dot_length = 0.3*space_length;
|
|
double dash_length = 1.5*space_length;
|
|
|
|
QString dash = "on " + QString::number(dash_length) + "cm";
|
|
QString dot = "on " + QString::number(dot_length) + "cm";
|
|
QString space = " off " + QString::number(space_length) + "cm";
|
|
|
|
QString aux = "dash pattern=";
|
|
QString term = ", dash phase=0pt, ";
|
|
switch (pen.style()){
|
|
case Qt::SolidLine:
|
|
break;
|
|
case Qt::DashLine:
|
|
options += aux + dash + space + term;
|
|
break;
|
|
case Qt::DotLine:
|
|
options += aux + dot + space + term;
|
|
break;
|
|
case Qt::DashDotLine:
|
|
options += aux + dash + space + dot + space + term;
|
|
break;
|
|
case Qt::DashDotDotLine:
|
|
options += aux + dash + space + dot + space + dot + space + term;
|
|
break;
|
|
|
|
case Qt::CustomDashLine:
|
|
{
|
|
options += aux;
|
|
QVector<qreal> pattern = pen.dashPattern();
|
|
int count = pattern.count();
|
|
QString u = unit();
|
|
for (int i = 0; i < count; i++){
|
|
QString s = "on ";
|
|
if (i%2)
|
|
s = " off ";
|
|
options += s + QString::number(pattern[i]) + u;
|
|
}
|
|
|
|
options += term;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
options += "line join=";
|
|
switch (pen.joinStyle()){
|
|
case Qt::MiterJoin:
|
|
options += "miter, ";
|
|
break;
|
|
case Qt::BevelJoin:
|
|
options += "bevel, ";
|
|
break;
|
|
case Qt::RoundJoin:
|
|
options += "round, ";
|
|
break;
|
|
case Qt::SvgMiterJoin:
|
|
options += "miter, ";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
options += "line cap=";
|
|
switch (pen.capStyle()){
|
|
case Qt::FlatCap:
|
|
options += "rect]";
|
|
break;
|
|
case Qt::SquareCap:
|
|
options += "rect]";
|
|
break;
|
|
case Qt::RoundCap:
|
|
options += "round]";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return col + "\\draw" + options;
|
|
}
|
|
|
|
QString QTeXPaintEngine::indentString(const QString& s)
|
|
{
|
|
QStringList lst = s.split("\n", QString::SkipEmptyParts);
|
|
for(int i = 0; i < lst.count(); i++)
|
|
lst[i].prepend("\t");
|
|
|
|
return lst.join("\n") + "\n";
|
|
}
|
|
|
|
QString QTeXPaintEngine::beginScope()
|
|
{
|
|
QString s = "\\begin{scope}\n";
|
|
if (d_pgf_mode)
|
|
s = "\\begin{pgfscope}\n";
|
|
|
|
if (painter()->hasClipping()){
|
|
QString clip = clipPath();
|
|
if (!clip.isEmpty())
|
|
s += indentString(clip);
|
|
}
|
|
|
|
d_pattern_color = QColor();
|
|
d_current_color = QColor();
|
|
return s;
|
|
}
|
|
|
|
QString QTeXPaintEngine::endScope()
|
|
{
|
|
if (d_pgf_mode)
|
|
return "\\end{pgfscope}\n";
|
|
|
|
return "\\end{scope}\n";
|
|
}
|
|
|
|
void QTeXPaintEngine::writeToFile(const QString& s)
|
|
{
|
|
QTextStream t(file);
|
|
t.setCodec("UTF-8");
|
|
|
|
if (d_pgf_mode){
|
|
t << beginScope();
|
|
t << indentString(s);
|
|
t << endScope();
|
|
return;
|
|
}
|
|
|
|
QString scope;
|
|
if (d_open_scope)
|
|
scope = endScope();
|
|
|
|
if (changedClipping()){
|
|
scope += beginScope();
|
|
scope += indentString(s);
|
|
t << scope;
|
|
|
|
d_open_scope = true;
|
|
|
|
if (painter()->hasClipping())
|
|
d_clip_path = painter()->clipPath();
|
|
else
|
|
d_clip_path = QPainterPath();
|
|
} else
|
|
t << indentString(s);
|
|
}
|
|
|
|
bool QTeXPaintEngine::emptyStringOperation()
|
|
{
|
|
if ((painter()->brush().style() == Qt::NoBrush ||
|
|
(painter()->brush().color().alpha() == 0)) &&
|
|
painter()->pen().style() == Qt::NoPen)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool QTeXPaintEngine::changedClipping()
|
|
{
|
|
QPainterPath clipPath = QPainterPath();
|
|
if (painter()->hasClipping()){
|
|
if (painter()->clipPath().elementCount() > 1000)
|
|
return false;
|
|
clipPath = painter()->clipPath();
|
|
}
|
|
|
|
if (clipPath != d_clip_path)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool QTeXPaintEngine::addNewPatternColor()
|
|
{
|
|
Qt::BrushStyle style = painter()->brush().style();
|
|
if (style <= Qt::SolidPattern || style >= Qt::LinearGradientPattern)
|
|
return false;
|
|
|
|
if (!d_pattern_color.isValid() ||
|
|
d_pattern_color != painter()->brush().color())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool QTeXPaintEngine::addNewBrushColor()
|
|
{
|
|
if (!d_current_color.isValid() || changedClipping() ||
|
|
d_current_color.name() != painter()->brush().color().name())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool QTeXPaintEngine::addNewPenColor()
|
|
{
|
|
if (!d_current_color.isValid() ||
|
|
(changedClipping() && painter()->brush().style() == Qt::NoBrush) ||
|
|
d_current_color.name() != painter()->pen().color().name())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
QString QTeXPaintEngine::unit()
|
|
{
|
|
switch (d_unit){
|
|
case QTeXPaintDevice::pt:
|
|
return "pt";
|
|
break;
|
|
case QTeXPaintDevice::bp:
|
|
return "bp";
|
|
break;
|
|
case QTeXPaintDevice::mm:
|
|
return "mm";
|
|
break;
|
|
case QTeXPaintDevice::cm:
|
|
return "cm";
|
|
break;
|
|
case QTeXPaintDevice::in:
|
|
return "in";
|
|
break;
|
|
case QTeXPaintDevice::ex:
|
|
return "ex";
|
|
break;
|
|
case QTeXPaintDevice::em:
|
|
return "em";
|
|
break;
|
|
}
|
|
return "pt";
|
|
}
|