mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-02-02 10:27:12 +01:00
228 lines
7.1 KiB
C++
228 lines
7.1 KiB
C++
// Copyright (C) 2021-2024 Jakub Melka
|
|
//
|
|
// This file is part of PDF4QT.
|
|
//
|
|
// PDF4QT is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// with the written consent of the copyright owner, any later version.
|
|
//
|
|
// PDF4QT 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 Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
#include "pdfpainterutils.h"
|
|
#include "pdfpagecontentprocessor.h"
|
|
|
|
#include <QPainterPath>
|
|
#include <QFontMetrics>
|
|
|
|
#include "pdfdbgheap.h"
|
|
|
|
namespace pdf
|
|
{
|
|
|
|
QRect PDFPainterHelper::drawBubble(QPainter* painter, QPoint point, QColor color, QString text, Qt::Alignment alignment)
|
|
{
|
|
QFontMetrics fontMetrics = painter->fontMetrics();
|
|
|
|
const int lineSpacing = fontMetrics.lineSpacing();
|
|
const int bubbleHeight = lineSpacing* 2;
|
|
const int bubbleWidth = lineSpacing + fontMetrics.horizontalAdvance(text);
|
|
|
|
QRect rectangle(point, QSize(bubbleWidth, bubbleHeight));
|
|
|
|
if (alignment.testFlag(Qt::AlignVCenter))
|
|
{
|
|
rectangle.translate(0, -rectangle.height() / 2);
|
|
}
|
|
else if (alignment.testFlag(Qt::AlignTop))
|
|
{
|
|
rectangle.translate(0, -rectangle.height());
|
|
}
|
|
|
|
if (alignment.testFlag(Qt::AlignHCenter))
|
|
{
|
|
rectangle.translate(-rectangle.width() / 2, 0);
|
|
}
|
|
else if (alignment.testFlag(Qt::AlignLeft))
|
|
{
|
|
rectangle.translate(-rectangle.width(), 0);
|
|
}
|
|
|
|
PDFPainterStateGuard guard(painter);
|
|
painter->setRenderHint(QPainter::Antialiasing);
|
|
painter->setPen(Qt::NoPen);
|
|
painter->setBrush(QBrush(color));
|
|
painter->drawRoundedRect(rectangle, rectangle.height() / 2, rectangle.height() / 2, Qt::AbsoluteSize);
|
|
painter->setPen(Qt::black);
|
|
painter->drawText(rectangle, Qt::AlignCenter, text);
|
|
|
|
return rectangle;
|
|
}
|
|
|
|
QPen PDFPainterHelper::createPenFromState(const PDFPageContentProcessorState* graphicState, double alpha)
|
|
{
|
|
QColor color = graphicState->getStrokeColor();
|
|
if (color.isValid())
|
|
{
|
|
color.setAlphaF(alpha);
|
|
const PDFReal lineWidth = graphicState->getLineWidth();
|
|
Qt::PenCapStyle penCapStyle = graphicState->getLineCapStyle();
|
|
Qt::PenJoinStyle penJoinStyle = graphicState->getLineJoinStyle();
|
|
const PDFLineDashPattern& lineDashPattern = graphicState->getLineDashPattern();
|
|
const PDFReal mitterLimit = graphicState->getMitterLimit();
|
|
|
|
QPen pen(color);
|
|
|
|
pen.setWidthF(lineWidth);
|
|
pen.setCapStyle(penCapStyle);
|
|
pen.setJoinStyle(penJoinStyle);
|
|
pen.setMiterLimit(mitterLimit);
|
|
|
|
if (lineDashPattern.isSolid())
|
|
{
|
|
pen.setStyle(Qt::SolidLine);
|
|
}
|
|
else
|
|
{
|
|
pen.setStyle(Qt::CustomDashLine);
|
|
pen.setDashPattern(lineDashPattern.createForQPen(pen.widthF()));
|
|
pen.setDashOffset(lineDashPattern.getDashOffset());
|
|
}
|
|
|
|
return pen;
|
|
}
|
|
else
|
|
{
|
|
return QPen(Qt::NoPen);
|
|
}
|
|
}
|
|
|
|
QBrush PDFPainterHelper::createBrushFromState(const PDFPageContentProcessorState* graphicState, double alpha)
|
|
{
|
|
QColor color = graphicState->getFillColor();
|
|
if (color.isValid())
|
|
{
|
|
color.setAlphaF(alpha);
|
|
return QBrush(color, Qt::SolidPattern);
|
|
}
|
|
else
|
|
{
|
|
return QBrush(Qt::NoBrush);
|
|
}
|
|
}
|
|
|
|
void PDFPainterHelper::applyPenToGraphicState(PDFPageContentProcessorState* graphicState, const QPen& pen)
|
|
{
|
|
if (pen.style() != Qt::NoPen)
|
|
{
|
|
graphicState->setLineWidth(pen.widthF());
|
|
graphicState->setLineCapStyle(pen.capStyle());
|
|
graphicState->setLineJoinStyle(pen.joinStyle());
|
|
graphicState->setMitterLimit(pen.miterLimit());
|
|
|
|
QColor color = pen.color();
|
|
|
|
graphicState->setAlphaStroking(color.alphaF());
|
|
|
|
const PDFAbstractColorSpace* strokeColorSpace = graphicState->getStrokeColorSpace();
|
|
if (!strokeColorSpace || strokeColorSpace->getColorSpace() != PDFAbstractColorSpace::ColorSpace::DeviceRGB)
|
|
{
|
|
graphicState->setStrokeColorSpace(QSharedPointer<PDFAbstractColorSpace>(new PDFDeviceRGBColorSpace()));
|
|
}
|
|
graphicState->setStrokeColor(color, PDFColor(color.redF(), color.greenF(), color.blueF()));
|
|
|
|
if (pen.style() == Qt::SolidLine)
|
|
{
|
|
graphicState->setLineDashPattern(PDFLineDashPattern());
|
|
}
|
|
else
|
|
{
|
|
// TODO: Line Dash Pattern
|
|
/*
|
|
pen.setStyle(Qt::CustomDashLine);
|
|
pen.setDashPattern(lineDashPattern.createForQPen(pen.widthF()));
|
|
pen.setDashOffset(lineDashPattern.getDashOffset());*/
|
|
}
|
|
}
|
|
}
|
|
|
|
void PDFPainterHelper::applyBrushToGraphicState(PDFPageContentProcessorState* graphicState, const QBrush& brush)
|
|
{
|
|
if (brush.style() != Qt::NoBrush)
|
|
{
|
|
QColor color = brush.color();
|
|
|
|
graphicState->setAlphaFilling(color.alphaF());
|
|
|
|
const PDFAbstractColorSpace* fillColorSpace = graphicState->getFillColorSpace();
|
|
if (!fillColorSpace || fillColorSpace->getColorSpace() != PDFAbstractColorSpace::ColorSpace::DeviceRGB)
|
|
{
|
|
graphicState->setFillColorSpace(QSharedPointer<PDFAbstractColorSpace>(new PDFDeviceRGBColorSpace()));
|
|
}
|
|
graphicState->setFillColor(color, PDFColor(color.redF(), color.greenF(), color.blueF()));
|
|
}
|
|
}
|
|
|
|
PDFTransformationDecomposition PDFPainterHelper::decomposeTransform(const QTransform& transform)
|
|
{
|
|
PDFTransformationDecomposition result;
|
|
|
|
const qreal m11 = transform.m11();
|
|
const qreal m12 = transform.m12();
|
|
const qreal m21 = transform.m21();
|
|
const qreal m22 = transform.m22();
|
|
|
|
const qreal dx = transform.dx();
|
|
const qreal dy = transform.dy();
|
|
|
|
const qreal sx = std::sqrt(m11 * m11 + m21 * m21);
|
|
const qreal phi = std::atan2(m21, m11);
|
|
const qreal msy = m12 * std::cos(phi) + m22 * std::sin(phi);
|
|
const qreal sy = -m12 * std::sin(phi) + m22 * std::cos(phi);
|
|
|
|
result.rotationAngle = phi;
|
|
result.scaleX = sx;
|
|
result.scaleY = sy;
|
|
|
|
if (!qFuzzyIsNull(sy))
|
|
{
|
|
result.shearFactor = msy / sy;
|
|
}
|
|
else
|
|
{
|
|
result.shearFactor = 0.0;
|
|
}
|
|
|
|
result.translateX = dx;
|
|
result.translateY = dy;
|
|
|
|
return result;
|
|
}
|
|
|
|
QTransform PDFPainterHelper::composeTransform(const PDFTransformationDecomposition& decomposition)
|
|
{
|
|
const qreal s = std::sin(decomposition.rotationAngle);
|
|
const qreal c = std::cos(decomposition.rotationAngle);
|
|
|
|
const qreal m = decomposition.shearFactor;
|
|
const qreal sx = decomposition.scaleX;
|
|
const qreal sy = decomposition.scaleY;
|
|
const qreal dx = decomposition.translateX;
|
|
const qreal dy = decomposition.translateY;
|
|
|
|
const qreal m11 = sx * c;
|
|
const qreal m12 = sy * m * c - sy * s;
|
|
const qreal m21 = sx * s;
|
|
const qreal m22 = sy * m * s + sy * c;
|
|
|
|
return QTransform(m11, m12, m21, m22, dx, dy);
|
|
}
|
|
|
|
} // namespace pdf
|