r23536 by jghali - #16073: Fix some clip handling issues in PDF import <Pontobart>

scribus-commit scribus-commit at lists.scribus.net
Thu Mar 26 00:25:23 UTC 2020


Author: jghali
Date: Thu Mar 26 00:25:22 2020
New Revision: 23536

URL: http://scribus.net/websvn/listing.php?repname=Scribus&sc=1&rev=23536
Log:
#16073: Fix some clip handling issues in PDF import <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=23536&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	Thu Mar 26 00:25:22 2020
@@ -1298,8 +1298,7 @@
 	m_actPage = pageNum;
 	m_groupStack.clear();
 	pushGroup();
-	m_currentClipPath.resize(0);
-	m_currentClipPath.svgInit();
+	m_currentClipPath = QPainterPath();
 	m_clipPaths.clear();
 }
 
@@ -1358,10 +1357,10 @@
 				PageItem *ite = m_doc->groupObjectsSelection(tmpSel);
 				if (ite)
 				{
-					FPointArray out = m_currentClipPath.copy();
-					out.translate(m_doc->currentPage()->xOffset(), m_doc->currentPage()->yOffset());
-					out.translate(-ite->xPos(), -ite->yPos());
-					ite->PoLine = out.copy();
+					QPainterPath clippath = m_currentClipPath;
+					clippath.translate(m_doc->currentPage()->xOffset(), m_doc->currentPage()->yOffset());
+					clippath.translate(-ite->xPos(), -ite->yPos());
+					ite->PoLine.fromQPainterPath(clippath, true);
 					ite->ClipEdited = true;
 					ite->FrameType = 3;
 					ite->setTextFlowMode(PageItem::TextFlowDisabled);
@@ -1486,10 +1485,10 @@
 		ite->FrameType = 3;
 		if (checkClip())
 		{
-			FPointArray out = m_currentClipPath.copy();
-			out.translate(m_doc->currentPage()->xOffset(), m_doc->currentPage()->yOffset());
-			out.translate(-ite->xPos(), -ite->yPos());
-			ite->PoLine = out.copy();
+			QPainterPath clippath = m_currentClipPath;
+			clippath.translate(m_doc->currentPage()->xOffset(), m_doc->currentPage()->yOffset());
+			clippath.translate(-ite->xPos(), -ite->yPos());
+			ite->PoLine.fromQPainterPath(clippath, true);
 			ite->ClipEdited = true;
 			ite->FrameType = 3;
 			ite->setTextFlowMode(PageItem::TextFlowDisabled);
@@ -1557,75 +1556,39 @@
 void SlaOutputDev::clip(GfxState *state)
 {
 //	qDebug() << "Clip";
-	const double *ctm;
-	ctm = state->getCTM();
+	adjustClip(state, Qt::WindingFill);
+}
+
+void SlaOutputDev::eoClip(GfxState *state)
+{
+//	qDebug() << "EoClip";
+	adjustClip(state, Qt::OddEvenFill);
+}
+
+void SlaOutputDev::adjustClip(GfxState *state, Qt::FillRule fillRule)
+{
+	const double *ctm = state->getCTM();
 	m_ctm = QTransform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]);
 	QString output = convertPath(state->getPath());
 	if (output.isEmpty())
 		return;
-
 	FPointArray out;
 	out.parseSVG(output);
 	out.svgClosePath();
 	out.map(m_ctm);
 	if (checkClip())
 	{
-		QPainterPath pathN = out.toQPainterPath(true);
-		QPainterPath pathA = m_currentClipPath.toQPainterPath(true);
-		QPainterPath resultPath = pathA.intersected(pathN);
-		if (!resultPath.isEmpty())
-		{
-			FPointArray polyline;
-			polyline.resize(0);
-			polyline.fromQPainterPath(resultPath, true);
-			polyline.svgClosePath();
-			m_currentClipPath = polyline.copy();
-		}
-		else
-		{
-			m_currentClipPath.resize(0);
-			m_currentClipPath.svgInit();
-		}
+		// "clip" (WindingFill) and "eoClip" (OddEvenFill) only the determine
+		// the fill rule of the new clipping path. The new clip should be the
+		// intersection of the old and new area. QPainterPath determines on
+		// its own which fill rule to use for the result. We should not loose
+		// this information.
+		QPainterPath pathN = out.toQPainterPath(false);
+		pathN.setFillRule(fillRule);
+		m_currentClipPath = pathN.intersected(m_currentClipPath);
 	}
 	else
-		m_currentClipPath = out.copy();
-}
-
-void SlaOutputDev::eoClip(GfxState *state)
-{
-//	qDebug() << "EoClip";
-	const double *ctm;
-	ctm = state->getCTM();
-	m_ctm = QTransform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]);
-	QString output = convertPath(state->getPath());
-	FPointArray out;
-	if (!output.isEmpty())
-	{
-		out.parseSVG(output);
-		out.svgClosePath();
-		out.map(m_ctm);
-		if (checkClip())
-		{
-			QPainterPath pathN = out.toQPainterPath(true);
-			QPainterPath pathA = m_currentClipPath.toQPainterPath(true);
-			QPainterPath resultPath = pathA.intersected(pathN);
-			if (!resultPath.isEmpty())
-			{
-				FPointArray polyline;
-				polyline.resize(0);
-				polyline.fromQPainterPath(resultPath, true);
-				polyline.svgClosePath();
-				m_currentClipPath = polyline.copy();
-			}
-			else
-			{
-				m_currentClipPath.resize(0);
-				m_currentClipPath.svgInit();
-			}
-		}
-		else
-			m_currentClipPath = out.copy();
-	}
+		m_currentClipPath = out.toQPainterPath(false);
 }
 
 void SlaOutputDev::stroke(GfxState *state)
@@ -1723,6 +1686,17 @@
 void SlaOutputDev::fill(GfxState *state)
 {
 //	qDebug() << "Fill";
+	createFillItem(state, Qt::WindingFill);
+}
+
+void SlaOutputDev::eoFill(GfxState *state)
+{
+//	qDebug() << "EoFill";
+	createFillItem(state, Qt::OddEvenFill);
+}
+
+void SlaOutputDev::createFillItem(GfxState *state, Qt::FillRule fillRule)
+{
 	const double *ctm;
 	ctm = state->getCTM();
 	double xCoor = m_doc->currentPage()->xOffset();
@@ -1732,9 +1706,15 @@
 	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);
+
 	Coords = output;
-	FPoint wh = out.widthHeight();
-	if ((out.size() > 3) && ((wh.x() != 0.0) || (wh.y() != 0.0)))
+	QRectF bbox = clippedPath.boundingRect();
+	if (!clippedPath.isEmpty() && !bbox.isNull())
 	{
 		CurrColorFill = getColor(state->getFillColorSpace(), state->getFillColor(), &CurrFillShade);
 		int z;
@@ -1743,17 +1723,20 @@
 		else
 			z = m_doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, xCoor, yCoor, 10, 10, 0, CurrColorFill, CommonStrings::None);
 		PageItem* ite = m_doc->Items->at(z);
-		ite->PoLine = out.copy();
+		ite->PoLine.fromQPainterPath(clippedPath, true);
 		ite->ClipEdited = true;
 		ite->FrameType = 3;
 		ite->setFillShade(CurrFillShade);
 		ite->setLineShade(100);
-		ite->setFillEvenOdd(false);
+		// 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.
+		ite->setFillEvenOdd(clippedPath.fillRule() == Qt::OddEvenFill);
 		ite->setFillTransparency(1.0 - state->getFillOpacity());
 		ite->setFillBlendmode(getBlendMode(state));
 		ite->setLineEnd(PLineEnd);
 		ite->setLineJoin(PLineJoin);
-		ite->setWidthHeight(wh.x(),wh.y());
+		ite->setWidthHeight(bbox.width(),bbox.height());
 		ite->setTextFlowMode(PageItem::TextFlowDisabled);
 		m_doc->adjustItemSize(ite);
 		m_Elements->append(ite);
@@ -1765,53 +1748,9 @@
 	}
 }
 
-void SlaOutputDev::eoFill(GfxState *state)
-{
-//	qDebug() << "EoFill";
-	const double *ctm;
-	ctm = state->getCTM();
-	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);
-	Coords = output;
-	FPoint wh = out.widthHeight();
-	if ((out.size() > 3) && ((wh.x() != 0.0) || (wh.y() != 0.0)))
-	{
-		CurrColorFill = getColor(state->getFillColorSpace(), state->getFillColor(), &CurrFillShade);
-		int z;
-		if (pathIsClosed)
-			z = m_doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, xCoor, yCoor, 10, 10, 0, CurrColorFill, CommonStrings::None);
-		else
-			z = m_doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, xCoor, yCoor, 10, 10, 0, CurrColorFill, CommonStrings::None);
-		PageItem* ite = m_doc->Items->at(z);
-		ite->PoLine = out.copy();
-		ite->ClipEdited = true;
-		ite->FrameType = 3;
-		ite->setFillShade(CurrFillShade);
-		ite->setLineShade(100);
-		ite->setFillEvenOdd(true);
-		ite->setFillTransparency(1.0 - state->getFillOpacity());
-		ite->setFillBlendmode(getBlendMode(state));
-		ite->setLineEnd(PLineEnd);
-		ite->setLineJoin(PLineJoin);
-		ite->setWidthHeight(wh.x(),wh.y());
-		ite->setTextFlowMode(PageItem::TextFlowDisabled);
-		m_doc->adjustItemSize(ite);
-		m_Elements->append(ite);
-		if (m_groupStack.count() != 0)
-		{
-			m_groupStack.top().Items.append(ite);
-			applyMask(ite);
-		}
-	}
-}
-
 GBool SlaOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax)
 {
+//	qDebug() << "SlaOutputDev::axialShadedFill";
 	double GrStartX;
 	double GrStartY;
 	double GrEndX;
@@ -1884,16 +1823,16 @@
 	PageItem* ite = m_doc->Items->at(z);
 	if (checkClip())
 	{
-		FPointArray out = m_currentClipPath.copy();
+		QPainterPath out = m_currentClipPath;
 		out.translate(m_doc->currentPage()->xOffset(), m_doc->currentPage()->yOffset());
 		out.translate(-ite->xPos(), -ite->yPos());
-		ite->PoLine = out.copy();
+		ite->PoLine.fromQPainterPath(out, true);
+		ite->setFillEvenOdd(out.fillRule() == Qt::OddEvenFill);
 	}
 	ite->ClipEdited = true;
 	ite->FrameType = 3;
 	ite->setFillShade(CurrFillShade);
 	ite->setLineShade(100);
-	ite->setFillEvenOdd(false);
 	ite->setFillTransparency(1.0 - state->getFillOpacity());
 	ite->setFillBlendmode(getBlendMode(state));
 	ite->setLineEnd(PLineEnd);
@@ -1924,6 +1863,7 @@
 
 GBool SlaOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax)
 {
+//	qDebug() << "SlaOutputDev::radialShadedFill";
 	double GrStartX;
 	double GrStartY;
 	double GrEndX;
@@ -2004,16 +1944,16 @@
 	PageItem* ite = m_doc->Items->at(z);
 	if (checkClip())
 	{
-		FPointArray out = m_currentClipPath.copy();
+		QPainterPath out = m_currentClipPath;
 		out.translate(m_doc->currentPage()->xOffset(), m_doc->currentPage()->yOffset());
 		out.translate(-ite->xPos(), -ite->yPos());
-		ite->PoLine = out.copy();
+		ite->PoLine.fromQPainterPath(out, true);
+		ite->setFillEvenOdd(out.fillRule() == Qt::OddEvenFill);
 	}
 	ite->ClipEdited = true;
 	ite->FrameType = 3;
 	ite->setFillShade(CurrFillShade);
 	ite->setLineShade(100);
-	ite->setFillEvenOdd(false);
 	ite->setFillTransparency(1.0 - state->getFillOpacity());
 	ite->setFillBlendmode(getBlendMode(state));
 	ite->setLineEnd(PLineEnd);
@@ -2044,6 +1984,7 @@
 
 GBool SlaOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
 {
+//	qDebug() << "SlaOutputDev::gouraudTriangleShadedFill";
 	double xCoor = m_doc->currentPage()->xOffset();
 	double yCoor = m_doc->currentPage()->yOffset();
 	double xmin, ymin, xmax, ymax;
@@ -2123,7 +2064,7 @@
 
 GBool SlaOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading)
 {
-//	qDebug() << "mesh shaded fill";
+//	qDebug() << "SlaOutputDev::patchMeshShadedFill";
 	double xCoor = m_doc->currentPage()->xOffset();
 	double yCoor = m_doc->currentPage()->yOffset();
 	double xmin, ymin, xmax, ymax;
@@ -2272,6 +2213,7 @@
 
 GBool SlaOutputDev::tilingPatternFill(GfxState *state, Gfx * /*gfx*/, Catalog *cat, Object *str, POPPLER_CONST_070 double *pmat, int paintType, int tilingType, Dict *resDict, POPPLER_CONST_070 double *mat, POPPLER_CONST_070 double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep)
 {
+//	qDebug() << "SlaOutputDev::tilingPatternFill";
 	PDFRectangle box;
 	Gfx *gfx;
 	QString id;
@@ -2360,16 +2302,16 @@
 	ite = m_doc->Items->at(z);
 	if (checkClip())
 	{
-		FPointArray out = m_currentClipPath.copy();
+		QPainterPath out = m_currentClipPath;
 		out.translate(m_doc->currentPage()->xOffset(), m_doc->currentPage()->yOffset());
 		out.translate(-ite->xPos(), -ite->yPos());
-		ite->PoLine = out.copy();
+		ite->PoLine.fromQPainterPath(out, true);
+		ite->setFillEvenOdd(out.fillRule() == Qt::OddEvenFill);
 	}
 	ite->ClipEdited = true;
 	ite->FrameType = 3;
 	ite->setFillShade(CurrFillShade);
 	ite->setLineShade(100);
-	ite->setFillEvenOdd(false);
 	ite->setFillTransparency(1.0 - state->getFillOpacity());
 	ite->setFillBlendmode(getBlendMode(state));
 	ite->setLineEnd(PLineEnd);
@@ -2700,6 +2642,17 @@
 		torigin = m_ctm.map(QPointF(0, 1));
 	}
 
+	// Determine the visible area of the picture after clipping it. If it is empty, no item
+	// needs to be created.
+	QPainterPath outline;
+	outline.addRect(0, 0, 1, 1);
+	outline = m_ctm.map(outline);
+	if (!m_currentClipPath.isEmpty())
+		outline = outline.intersected(m_currentClipPath);
+
+	if ((inPattern == 0) && (outline.isEmpty() || outline.boundingRect().isNull()))
+		return;
+
     // Determine the width and height of the image by undoing the rotation part
 	// of the CTM and applying the result to the unit square.
 	QTransform without_rotation; 
@@ -2798,20 +2751,15 @@
 		}
 		delete tempFile;
 	}
-	if ((checkClip()) && (inPattern == 0))
-	{
-		FPointArray out = m_currentClipPath.copy();
+	if (inPattern == 0)
+	{
+		outline.translate(xCoor - ite->xPos(), yCoor - ite->yPos());
+		// Undo the rotation of the clipping path as it is rotated together with the iamge.
 		QTransform mm;
 		mm.rotate(-ite->rotation());
-		out.translate(m_doc->currentPage()->xOffset(), m_doc->currentPage()->yOffset());
-		out.translate(-ite->xPos(), -ite->yPos());
-		// Undo the rotation of the clipping path as it is rotated together with the iamge.
-		for (int i = 0; i != out.size(); ++i)
-		{
-			QPointF p = mm.map(out.pointQF(i));
-			out.setPoint(i, p.x(), p.y());
-		}
-		ite->PoLine = out.copy();
+		outline = mm.map(outline);
+		ite->PoLine.fromQPainterPath(outline, true);
+		ite->setFillEvenOdd(outline.fillRule() == Qt::OddEvenFill);
 		ite->ClipEdited = true;
 		ite->FrameType = 3;
 		ite->setTextFlowMode(PageItem::TextFlowDisabled);
@@ -3536,6 +3484,7 @@
 
 QString SlaOutputDev::convertPath(POPPLER_CONST_083 GfxPath *path)
 {
+//	qDebug() << "SlaOutputDev::convertPath";
 	if (! path)
 		return QString();
 
@@ -3798,10 +3747,10 @@
 bool SlaOutputDev::checkClip()
 {
 	bool ret = false;
-	if (m_currentClipPath.count() != 0)
-	{
-		FPoint wh = m_currentClipPath.widthHeight();
-		if ((wh.x() > 0) && (wh.y() > 0))
+	if (!m_currentClipPath.isEmpty())
+	{
+		QRectF bbox = m_currentClipPath.boundingRect();
+		if ((bbox.width() > 0) && (bbox.height() > 0))
 			ret = true;
 	}
 	return ret;

Modified: trunk/Scribus/scribus/plugins/import/pdf/slaoutput.h
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=23536&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	Thu Mar 26 00:25:22 2020
@@ -296,6 +296,14 @@
 	QString UnicodeParsedString(const std::string& s1);
 	bool checkClip();
 
+	// Intersect the current clip path with the new path in state where filled areas
+	// are interpreted according to fillRule.
+	void adjustClip(GfxState *state, Qt::FillRule fillRule);
+
+	// Take the current path of state and interpret it according to fillRule,
+	// intersect it with the clipping path and create a new pageitem for it.
+	void createFillItem(GfxState *state, Qt::FillRule fillRule);
+
 	void createImageFrame(QImage& image, GfxState *state, int numColorComponents);
 
 	bool pathIsClosed {false};
@@ -308,8 +316,15 @@
 	QVector<double> DashValues;
 	double DashOffset {0.0};
 	QString Coords;
-	FPointArray m_currentClipPath;
-	QStack<FPointArray> m_clipPaths;
+	// The currently visible area. If it is empty, everything is visible.
+	// QPainterPath has the drawback that it sometimes approximates Bezier curves
+	// by line segments for numerical stability. If available, a better class
+	// should be used. However, it is important that the used class knows which
+	// areas are covered and does not rely on external information about the fill
+	// rule to use.
+	QPainterPath m_currentClipPath;
+	QStack<QPainterPath> m_clipPaths;
+
 	struct groupEntry
 	{
 		QList<PageItem*> Items;




More information about the scribus-commit mailing list