r23550 by jghali - #16077: Rotate imported PDF path's and tiling patterns <Pontobart>
scribus-commit
scribus-commit at lists.scribus.net
Tue Mar 31 14:53:40 UTC 2020
Author: jghali
Date: Tue Mar 31 14:53:40 2020
New Revision: 23550
URL: http://scribus.net/websvn/listing.php?repname=Scribus&sc=1&rev=23550
Log:
#16077: Rotate imported PDF path's and tiling patterns <Pontobart>
Modified:
trunk/Scribus/scribus/plugins/import/pdf/slaoutput.cpp
trunk/Scribus/scribus/plugins/import/pdf/slaoutput.h
Modified: trunk/Scribus/scribus/plugins/import/pdf/slaoutput.cpp
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=23550&path=/trunk/Scribus/scribus/plugins/import/pdf/slaoutput.cpp
==============================================================================
--- trunk/Scribus/scribus/plugins/import/pdf/slaoutput.cpp (original)
+++ trunk/Scribus/scribus/plugins/import/pdf/slaoutput.cpp Tue Mar 31 14:53:40 2020
@@ -19,6 +19,42 @@
#include "util.h"
#include "util_math.h"
#include <tiffio.h>
+
+namespace
+{
+ // Compute the intersection of two paths while considering the fillrule of each of them.
+ // QPainterPath has the right interface to do the operation but is currently buggy.
+ // See for example https://bugreports.qt.io/browse/QTBUG-83102. Thus this function
+ // applies some heuristics to find the best result. As soon QPainterPath is fixed
+ // one can just use a.intersected(b) wherever this function is called.
+ // TODO: Find an alternative to QPainterPath that works for different fill rules.
+ QPainterPath intersection(QPainterPath const &a, QPainterPath const &b)
+ {
+ // An empty path is treated like the whole area.
+ if (a.elementCount() == 0)
+ return b;
+ if (b.elementCount() == 0)
+ return a;
+
+ QPainterPath ret_a = a.intersected(b);
+ QPainterPath ret_b = b.intersected(a);
+ // Sometimes the resulting paths are not closed even though they should.
+ // Close them now.
+ ret_a.closeSubpath();
+ ret_b.closeSubpath();
+
+ // Most of the time one of the two operations returns an empty path while the other
+ // gives us the desired result. Return the non-empty one.
+ if (ret_a.elementCount() == 0)
+ return ret_b;
+ if (ret_b.elementCount() == 0)
+ return ret_a;
+
+ // There are cases where both intersections are not empty but one of them is quite
+ // complicated with several subpaths, etc. We return the simpler one.
+ return (ret_a.elementCount() <= ret_b.elementCount()) ? ret_a : ret_b;
+ }
+}
LinkSubmitForm::LinkSubmitForm(Object *actionObj)
{
@@ -1411,12 +1447,14 @@
void SlaOutputDev::beginTransparencyGroup(GfxState *state, POPPLER_CONST_070 double *bbox, GfxColorSpace * /*blendingColorSpace*/, GBool isolated, GBool knockout, GBool forSoftMask)
{
+// qDebug() << "SlaOutputDev::beginTransparencyGroup isolated:" << isolated << "knockout:" << knockout << "forSoftMask:" << forSoftMask;
pushGroup("", forSoftMask);
m_groupStack.top().isolated = isolated;
}
void SlaOutputDev::paintTransparencyGroup(GfxState *state, POPPLER_CONST_070 double *bbox)
{
+// qDebug() << "SlaOutputDev::paintTransparencyGroup";
if (m_groupStack.count() != 0)
{
if ((m_groupStack.top().Items.count() != 0) && (!m_groupStack.top().forSoftMask))
@@ -1430,6 +1468,7 @@
void SlaOutputDev::endTransparencyGroup(GfxState *state)
{
+// qDebug() << "SlaOutputDev::endTransparencyGroup";
if (m_groupStack.count() <= 0)
return;
@@ -1458,6 +1497,7 @@
m_doc->DoDrawing = false;
pat.width = ite->width();
pat.height = ite->height();
+ m_currentMaskPosition = QPointF(ite->xPos(), ite->yPos());
ite->gXpos = 0;
ite->gYpos = 0;
ite->setXYPos(ite->gXpos, ite->gYpos, true);
@@ -1528,8 +1568,8 @@
else
m_groupStack.top().inverted = true;
m_groupStack.top().maskName = m_currentMask;
- // Remember the mask's bounding box as it might not align with the image to which the mask is later assigned.
- m_groupStack.top().maskBBox = QRectF(QPointF(bbox[0], m_doc->currentPage()->height() - bbox[1]), QPointF(bbox[2], m_doc->currentPage()->height() - bbox[3]));
+ // Remember the mask's position as it might not align with the image to which the mask is later assigned.
+ m_groupStack.top().maskPos = m_currentMaskPosition;
m_groupStack.top().alpha = alpha;
if (m_groupStack.top().Items.count() != 0)
applyMask(m_groupStack.top().Items.last());
@@ -1585,7 +1625,7 @@
// this information.
QPainterPath pathN = out.toQPainterPath(false);
pathN.setFillRule(fillRule);
- m_currentClipPath = pathN.intersected(m_currentClipPath);
+ m_currentClipPath = intersection(pathN, m_currentClipPath);
}
else
m_currentClipPath = out.toQPainterPath(false);
@@ -1699,18 +1739,24 @@
{
const double *ctm;
ctm = state->getCTM();
+ m_ctm = QTransform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]);
double xCoor = m_doc->currentPage()->xOffset();
double yCoor = m_doc->currentPage()->yOffset();
FPointArray out;
QString output = convertPath(state->getPath());
out.parseSVG(output);
- m_ctm = QTransform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]);
out.map(m_ctm);
// Clip the new path first and only add it if it is not empty.
QPainterPath path = out.toQPainterPath(false);
path.setFillRule(fillRule);
- QPainterPath clippedPath = path.intersected(m_currentClipPath);
+ QPainterPath clippedPath = intersection(m_currentClipPath, path);
+
+ // Undo the rotation of the clipping path as it is rotated together with the item.
+ double angle = m_ctm.map(QLineF(0, 0, 1, 0)).angle();
+ QTransform mm;
+ mm.rotate(angle);
+ clippedPath = mm.map(clippedPath);
Coords = output;
QRectF bbox = clippedPath.boundingRect();
@@ -1728,6 +1774,7 @@
ite->FrameType = 3;
ite->setFillShade(CurrFillShade);
ite->setLineShade(100);
+ ite->setRotation(-angle);
// Only the new path has to be interpreted according to fillRule. QPainterPath
// could decide to create a final path according to the other rule. Thus
// we have to set this from the final path.
@@ -2242,7 +2289,11 @@
gfx = new Gfx(pdfDoc, this, resDict, &box, nullptr);
inPattern++;
+ // Unset the clip path as it is unrelated to the pattern's coordinate space.
+ QPainterPath savedClip = m_currentClipPath;
+ m_currentClipPath = QPainterPath();
gfx->display(str);
+ m_currentClipPath = savedClip;
inPattern--;
gElements = m_groupStack.pop();
m_doc->m_Selection->clear();
@@ -2300,13 +2351,20 @@
Coords = output;
int z = m_doc->itemAdd(PageItem::Polygon, PageItem::Rectangle, xCoor + crect.x(), yCoor + crect.y(), crect.width(), crect.height(), 0, CurrColorFill, CommonStrings::None);
ite = m_doc->Items->at(z);
+
+ m_ctm = QTransform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]);
+ double angle = m_ctm.map(QLineF(0, 0, 1, 0)).angle();
+ ite->setRotation(-angle);
if (checkClip())
{
- QPainterPath out = m_currentClipPath;
- out.translate(m_doc->currentPage()->xOffset(), m_doc->currentPage()->yOffset());
- out.translate(-ite->xPos(), -ite->yPos());
- ite->PoLine.fromQPainterPath(out, true);
- ite->setFillEvenOdd(out.fillRule() == Qt::OddEvenFill);
+ QPainterPath outline = m_currentClipPath;
+ outline.translate(xCoor - ite->xPos(), yCoor - ite->yPos());
+ // Undo the rotation of the clipping path as it is rotated together with the item.
+ QTransform mm;
+ mm.rotate(angle);
+ outline = mm.map(outline);
+ ite->PoLine.fromQPainterPath(outline, true);
+ ite->setFillEvenOdd(outline.fillRule() == Qt::OddEvenFill);
}
ite->ClipEdited = true;
ite->FrameType = 3;
@@ -2621,6 +2679,7 @@
void SlaOutputDev::createImageFrame(QImage& image, GfxState *state, int numColorComponents)
{
+// qDebug() << "SlaOutputDev::createImageFrame";
const double *ctm = state->getCTM();
double xCoor = m_doc->currentPage()->xOffset();
double yCoor = m_doc->currentPage()->yOffset();
@@ -2647,8 +2706,7 @@
QPainterPath outline;
outline.addRect(0, 0, 1, 1);
outline = m_ctm.map(outline);
- if (!m_currentClipPath.isEmpty())
- outline = m_currentClipPath.intersected(outline);
+ outline = intersection(outline, m_currentClipPath);
if ((inPattern == 0) && (outline.isEmpty() || outline.boundingRect().isNull()))
return;
@@ -3628,10 +3686,10 @@
if (!m_groupStack.top().maskName.isEmpty())
{
ite->setPatternMask(m_groupStack.top().maskName);
- QRectF bbox = m_groupStack.top().maskBBox;
+ QPointF maskPos = m_groupStack.top().maskPos;
double sx, sy, px, py, r, shx, shy;
ite->maskTransform(sx, sy, px, py, r, shx, shy);
- ite->setMaskTransform(sx, sy, bbox.x() - (ite->xPos() - m_doc->currentPage()->xOffset()) , bbox.y() - (ite->yPos() - m_doc->currentPage()->yOffset()) , r, shx, shy);
+ ite->setMaskTransform(sx, sy, maskPos.x() - ite->xPos(), maskPos.y() - ite->yPos(), r, shx, shy);
if (m_groupStack.top().alpha)
{
if (m_groupStack.top().inverted)
Modified: trunk/Scribus/scribus/plugins/import/pdf/slaoutput.h
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=23550&path=/trunk/Scribus/scribus/plugins/import/pdf/slaoutput.h
==============================================================================
--- trunk/Scribus/scribus/plugins/import/pdf/slaoutput.h (original)
+++ trunk/Scribus/scribus/plugins/import/pdf/slaoutput.h Tue Mar 31 14:53:40 2020
@@ -332,11 +332,12 @@
GBool isolated;
GBool alpha;
QString maskName;
- QRectF maskBBox;
+ QPointF maskPos;
bool inverted;
};
QStack<groupEntry> m_groupStack;
QString m_currentMask;
+ QPointF m_currentMaskPosition;
ScribusDoc* m_doc;
Selection* tmpSel;
QList<PageItem*> *m_Elements;
More information about the scribus-commit
mailing list