mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Tiling patterns (first part)
This commit is contained in:
@ -601,14 +601,14 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
|||||||
{
|
{
|
||||||
case PatternType::Tiling:
|
case PatternType::Tiling:
|
||||||
{
|
{
|
||||||
// TODO: Implement tiling pattern
|
const PDFTilingPattern* tilingPattern = pattern->getTilingPattern();
|
||||||
throw PDFParserException(PDFTranslationContext::tr("Tiling pattern not implemented."));
|
processTillingPatternPainting(tilingPattern, path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PatternType::Shading:
|
case PatternType::Shading:
|
||||||
{
|
{
|
||||||
const PDFShadingPattern* shadingPatern = pattern->getShadingPattern();
|
const PDFShadingPattern* shadingPattern = pattern->getShadingPattern();
|
||||||
|
|
||||||
// We must create a mesh and then draw pattern
|
// We must create a mesh and then draw pattern
|
||||||
PDFMeshQualitySettings settings;
|
PDFMeshQualitySettings settings;
|
||||||
@ -616,7 +616,7 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
|||||||
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
|
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
|
||||||
settings.initDefaultResolution();
|
settings.initDefaultResolution();
|
||||||
|
|
||||||
PDFMesh mesh = shadingPatern->createMesh(settings);
|
PDFMesh mesh = shadingPattern->createMesh(settings);
|
||||||
|
|
||||||
// Now, merge the current path to the mesh clipping path
|
// Now, merge the current path to the mesh clipping path
|
||||||
QPainterPath boundingPath = mesh.getBoundingPath();
|
QPainterPath boundingPath = mesh.getBoundingPath();
|
||||||
@ -660,8 +660,23 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
|||||||
{
|
{
|
||||||
case PatternType::Tiling:
|
case PatternType::Tiling:
|
||||||
{
|
{
|
||||||
// TODO: Implement tiling pattern
|
const PDFTilingPattern* tilingPattern = pattern->getTilingPattern();
|
||||||
throw PDFParserException(PDFTranslationContext::tr("Tiling pattern not implemented."));
|
|
||||||
|
// We must stroke the path.
|
||||||
|
QPainterPathStroker stroker;
|
||||||
|
stroker.setCapStyle(m_graphicState.getLineCapStyle());
|
||||||
|
stroker.setWidth(m_graphicState.getLineWidth());
|
||||||
|
stroker.setMiterLimit(m_graphicState.getMitterLimit());
|
||||||
|
stroker.setJoinStyle(m_graphicState.getLineJoinStyle());
|
||||||
|
|
||||||
|
const PDFLineDashPattern& lineDashPattern = m_graphicState.getLineDashPattern();
|
||||||
|
if (!lineDashPattern.isSolid())
|
||||||
|
{
|
||||||
|
stroker.setDashPattern(QVector<PDFReal>::fromStdVector(lineDashPattern.getDashArray()));
|
||||||
|
stroker.setDashOffset(lineDashPattern.getDashOffset());
|
||||||
|
}
|
||||||
|
QPainterPath strokedPath = stroker.createStroke(path);
|
||||||
|
processTillingPatternPainting(tilingPattern, strokedPath);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -730,6 +745,59 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::processTillingPatternPainting(const PDFTilingPattern* tilingPattern, const QPainterPath& path)
|
||||||
|
{
|
||||||
|
PDFPageContentProcessorStateGuard guard(this);
|
||||||
|
performClipping(path, path.fillRule());
|
||||||
|
|
||||||
|
// Initialize resources
|
||||||
|
const PDFObject& resources = tilingPattern->getResources();
|
||||||
|
if (!resources.isNull())
|
||||||
|
{
|
||||||
|
initDictionaries(resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(m_pagePointToDevicePointMatrix.isInvertible());
|
||||||
|
|
||||||
|
// Initialize rendering matrix
|
||||||
|
QMatrix patternMatrix = tilingPattern->getMatrix() * getPatternBaseMatrix();
|
||||||
|
QMatrix matrix = patternMatrix * m_pagePointToDevicePointMatrix.inverted();
|
||||||
|
QMatrix pathTransformationMatrix = m_graphicState.getCurrentTransformationMatrix() * matrix.inverted();
|
||||||
|
m_graphicState.setCurrentTransformationMatrix(matrix);
|
||||||
|
updateGraphicState();
|
||||||
|
|
||||||
|
// Tiling parameters
|
||||||
|
const QRectF tilingArea = pathTransformationMatrix.map(path).boundingRect();
|
||||||
|
const QRectF boundingBox = tilingPattern->getBoundingBox();
|
||||||
|
const PDFReal xStep = qAbs(tilingPattern->getXStep());
|
||||||
|
const PDFReal yStep = qAbs(tilingPattern->getYStep());
|
||||||
|
const QByteArray& content = tilingPattern->getContent();
|
||||||
|
QPainterPath boundingPath;
|
||||||
|
boundingPath.addRect(boundingBox);
|
||||||
|
|
||||||
|
// Draw the tiling
|
||||||
|
const PDFInteger columns = qMax<PDFInteger>(qCeil(tilingArea.width() / xStep), 1);
|
||||||
|
const PDFInteger rows = qMax<PDFInteger>(qCeil(tilingArea.height() / yStep), 1);
|
||||||
|
|
||||||
|
QMatrix baseTransformationMatrix = m_graphicState.getCurrentTransformationMatrix();
|
||||||
|
for (PDFInteger column = 0; column < columns; ++column)
|
||||||
|
{
|
||||||
|
for (PDFInteger row = 0; row < rows; ++row)
|
||||||
|
{
|
||||||
|
PDFPageContentProcessorGraphicStateSaveRestoreGuard guard(this);
|
||||||
|
|
||||||
|
QMatrix transformationMatrix = baseTransformationMatrix;
|
||||||
|
transformationMatrix.translate(tilingArea.left(), tilingArea.top());
|
||||||
|
transformationMatrix.translate(column * xStep, row * yStep);
|
||||||
|
m_graphicState.setCurrentTransformationMatrix(transformationMatrix);
|
||||||
|
updateGraphicState();
|
||||||
|
|
||||||
|
performClipping(boundingPath, boundingPath.fillRule());
|
||||||
|
processContent(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PDFPageContentProcessor::processCommand(const QByteArray& command)
|
void PDFPageContentProcessor::processCommand(const QByteArray& command)
|
||||||
{
|
{
|
||||||
Operator op = Operator::Invalid;
|
Operator op = Operator::Invalid;
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
class PDFMesh;
|
class PDFMesh;
|
||||||
|
class PDFTilingPattern;
|
||||||
class PDFOptionalContentActivity;
|
class PDFOptionalContentActivity;
|
||||||
|
|
||||||
static constexpr const char* PDF_RESOURCE_EXTGSTATE = "ExtGState";
|
static constexpr const char* PDF_RESOURCE_EXTGSTATE = "ExtGState";
|
||||||
@ -464,6 +465,11 @@ private:
|
|||||||
/// \param fillRule Fill rule used in the fill mode
|
/// \param fillRule Fill rule used in the fill mode
|
||||||
void processPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule);
|
void processPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule);
|
||||||
|
|
||||||
|
/// Performs tiling pattern painting
|
||||||
|
/// \param tilingPattern Tiling pattern to be painted
|
||||||
|
/// \param path Clipping path
|
||||||
|
void processTillingPatternPainting(const PDFTilingPattern* tilingPattern, const QPainterPath& path);
|
||||||
|
|
||||||
enum class MarkedContentKind
|
enum class MarkedContentKind
|
||||||
{
|
{
|
||||||
OptionalContent,
|
OptionalContent,
|
||||||
@ -505,6 +511,23 @@ private:
|
|||||||
const PDFDictionary* m_patternDictionary;
|
const PDFDictionary* m_patternDictionary;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PDFPageContentProcessorGraphicStateSaveRestoreGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline explicit PDFPageContentProcessorGraphicStateSaveRestoreGuard(PDFPageContentProcessor* processor) :
|
||||||
|
m_processor(processor)
|
||||||
|
{
|
||||||
|
m_processor->operatorSaveGraphicState();
|
||||||
|
}
|
||||||
|
inline ~PDFPageContentProcessorGraphicStateSaveRestoreGuard()
|
||||||
|
{
|
||||||
|
m_processor->operatorRestoreGraphicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFPageContentProcessor* m_processor;
|
||||||
|
};
|
||||||
|
|
||||||
/// Wrapper for PDF Name
|
/// Wrapper for PDF Name
|
||||||
struct PDFOperandName
|
struct PDFOperandName
|
||||||
{
|
{
|
||||||
|
@ -46,11 +46,22 @@ ShadingType PDFAxialShading::getShadingType() const
|
|||||||
PDFPatternPtr PDFPattern::createPattern(const PDFDictionary* colorSpaceDictionary, const PDFDocument* document, const PDFObject& object)
|
PDFPatternPtr PDFPattern::createPattern(const PDFDictionary* colorSpaceDictionary, const PDFDocument* document, const PDFObject& object)
|
||||||
{
|
{
|
||||||
const PDFObject& dereferencedObject = document->getObject(object);
|
const PDFObject& dereferencedObject = document->getObject(object);
|
||||||
|
const PDFDictionary* patternDictionary = nullptr;
|
||||||
|
QByteArray streamData;
|
||||||
|
|
||||||
if (dereferencedObject.isDictionary())
|
if (dereferencedObject.isDictionary())
|
||||||
{
|
{
|
||||||
PDFPatternPtr result;
|
patternDictionary = dereferencedObject.getDictionary();
|
||||||
|
}
|
||||||
|
else if (dereferencedObject.isStream())
|
||||||
|
{
|
||||||
|
const PDFStream* stream = dereferencedObject.getStream();
|
||||||
|
patternDictionary = stream->getDictionary();
|
||||||
|
streamData = document->getDecodedStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
const PDFDictionary* patternDictionary = dereferencedObject.getDictionary();
|
if (patternDictionary)
|
||||||
|
{
|
||||||
PDFDocumentDataLoaderDecorator loader(document);
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
|
||||||
const PatternType patternType = static_cast<PatternType>(loader.readIntegerFromDictionary(patternDictionary, "PatternType", static_cast<PDFInteger>(PatternType::Invalid)));
|
const PatternType patternType = static_cast<PatternType>(loader.readIntegerFromDictionary(patternDictionary, "PatternType", static_cast<PDFInteger>(PatternType::Invalid)));
|
||||||
@ -58,9 +69,43 @@ PDFPatternPtr PDFPattern::createPattern(const PDFDictionary* colorSpaceDictionar
|
|||||||
{
|
{
|
||||||
case PatternType::Tiling:
|
case PatternType::Tiling:
|
||||||
{
|
{
|
||||||
// TODO: Implement tiling pattern
|
const PDFTilingPattern::PaintType paintType = static_cast<PDFTilingPattern::PaintType>(loader.readIntegerFromDictionary(patternDictionary, "PaintType", static_cast<PDFInteger>(PDFTilingPattern::PaintType::Invalid)));
|
||||||
throw PDFParserException(PDFTranslationContext::tr("Tiling pattern not implemented."));
|
const PDFTilingPattern::TilingType tilingType = static_cast<PDFTilingPattern::TilingType>(loader.readIntegerFromDictionary(patternDictionary, "TilingType", static_cast<PDFInteger>(PDFTilingPattern::TilingType::Invalid)));
|
||||||
break;
|
const QRectF boundingBox = loader.readRectangle(patternDictionary->get("BBox"), QRectF());
|
||||||
|
const PDFReal xStep = loader.readNumberFromDictionary(patternDictionary, "XStep", 0.0);
|
||||||
|
const PDFReal yStep = loader.readNumberFromDictionary(patternDictionary, "YStep", 0.0);
|
||||||
|
PDFObject resources = document->getObject(patternDictionary->get("Resources"));
|
||||||
|
QMatrix matrix = loader.readMatrixFromDictionary(patternDictionary, "Matrix", QMatrix());
|
||||||
|
|
||||||
|
// Verify the data
|
||||||
|
if (paintType != PDFTilingPattern::PaintType::Colored && paintType != PDFTilingPattern::PaintType::Uncolored)
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid tiling pattern - wrong paint type %1.").arg(static_cast<PDFInteger>(paintType)));
|
||||||
|
}
|
||||||
|
if (tilingType != PDFTilingPattern::TilingType::ConstantSpacing && tilingType != PDFTilingPattern::TilingType::NoDistortion && tilingType != PDFTilingPattern::TilingType::ConstantSpacingAndFasterTiling)
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid tiling pattern - wrong tiling type %1.").arg(static_cast<PDFInteger>(tilingType)));
|
||||||
|
}
|
||||||
|
if (!boundingBox.isValid())
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid tiling pattern - bounding box is invalid.").arg(static_cast<PDFInteger>(paintType)));
|
||||||
|
}
|
||||||
|
if (isZero(xStep) || isZero(yStep))
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid tiling pattern - steps are invalid.").arg(static_cast<PDFInteger>(paintType)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFTilingPattern* pattern = new PDFTilingPattern();
|
||||||
|
pattern->m_boundingBox = boundingBox;
|
||||||
|
pattern->m_matrix = matrix;
|
||||||
|
pattern->m_paintType = paintType;
|
||||||
|
pattern->m_tilingType = tilingType;
|
||||||
|
pattern->m_xStep = xStep;
|
||||||
|
pattern->m_yStep = yStep;
|
||||||
|
pattern->m_resources = resources;
|
||||||
|
pattern->m_content = qMove(streamData);
|
||||||
|
|
||||||
|
return PDFPatternPtr(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
case PatternType::Shading:
|
case PatternType::Shading:
|
||||||
@ -74,7 +119,7 @@ PDFPatternPtr PDFPattern::createPattern(const PDFDictionary* colorSpaceDictionar
|
|||||||
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
|
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return PDFPatternPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
|
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
class PDFPattern;
|
class PDFPattern;
|
||||||
|
class PDFTilingPattern;
|
||||||
class PDFShadingPattern;
|
class PDFShadingPattern;
|
||||||
|
|
||||||
using PDFPatternPtr = std::shared_ptr<PDFPattern>;
|
using PDFPatternPtr = std::shared_ptr<PDFPattern>;
|
||||||
@ -193,6 +194,7 @@ public:
|
|||||||
|
|
||||||
virtual PatternType getType() const = 0;
|
virtual PatternType getType() const = 0;
|
||||||
virtual const PDFShadingPattern* getShadingPattern() const = 0;
|
virtual const PDFShadingPattern* getShadingPattern() const = 0;
|
||||||
|
virtual const PDFTilingPattern* getTilingPattern() const = 0;
|
||||||
|
|
||||||
/// Returns bounding box in the shadings target coordinate system (not in
|
/// Returns bounding box in the shadings target coordinate system (not in
|
||||||
/// pattern coordinate system).
|
/// pattern coordinate system).
|
||||||
@ -233,7 +235,50 @@ public:
|
|||||||
explicit PDFInvalidPattern() = default;
|
explicit PDFInvalidPattern() = default;
|
||||||
|
|
||||||
virtual PatternType getType() const { return PatternType::Invalid; }
|
virtual PatternType getType() const { return PatternType::Invalid; }
|
||||||
virtual const PDFShadingPattern* getShadingPattern() const { return nullptr; }
|
virtual const PDFShadingPattern* getShadingPattern() const override { return nullptr; }
|
||||||
|
virtual const PDFTilingPattern* getTilingPattern() const override { return nullptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFTilingPattern : public PDFPattern
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFTilingPattern() = default;
|
||||||
|
|
||||||
|
virtual PatternType getType() const override { return PatternType::Tiling; }
|
||||||
|
virtual const PDFShadingPattern* getShadingPattern() const override { return nullptr; }
|
||||||
|
virtual const PDFTilingPattern* getTilingPattern() const override { return this; }
|
||||||
|
|
||||||
|
enum class PaintType
|
||||||
|
{
|
||||||
|
Colored,
|
||||||
|
Uncolored,
|
||||||
|
Invalid
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TilingType
|
||||||
|
{
|
||||||
|
ConstantSpacing,
|
||||||
|
NoDistortion,
|
||||||
|
ConstantSpacingAndFasterTiling,
|
||||||
|
Invalid
|
||||||
|
};
|
||||||
|
|
||||||
|
PaintType getPaintingType() const { return m_paintType; }
|
||||||
|
TilingType getTilingType() const { return m_tilingType; }
|
||||||
|
PDFReal getXStep() const { return m_xStep; }
|
||||||
|
PDFReal getYStep() const { return m_yStep; }
|
||||||
|
const PDFObject& getResources() const { return m_resources; }
|
||||||
|
const QByteArray& getContent() const { return m_content; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class PDFPattern;
|
||||||
|
|
||||||
|
PaintType m_paintType = PaintType::Colored;
|
||||||
|
TilingType m_tilingType = TilingType::ConstantSpacing;
|
||||||
|
PDFReal m_xStep = 0.0;
|
||||||
|
PDFReal m_yStep = 0.0;
|
||||||
|
PDFObject m_resources;
|
||||||
|
QByteArray m_content;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Shading pattern - smooth color distribution along the pattern's space
|
/// Shading pattern - smooth color distribution along the pattern's space
|
||||||
@ -245,6 +290,7 @@ public:
|
|||||||
virtual PatternType getType() const override;
|
virtual PatternType getType() const override;
|
||||||
virtual ShadingType getShadingType() const = 0;
|
virtual ShadingType getShadingType() const = 0;
|
||||||
virtual const PDFShadingPattern* getShadingPattern() const override { return this; }
|
virtual const PDFShadingPattern* getShadingPattern() const override { return this; }
|
||||||
|
virtual const PDFTilingPattern* getTilingPattern() const override { return nullptr; }
|
||||||
|
|
||||||
/// Creates a colored mesh using settings. Mesh is generated in device space
|
/// Creates a colored mesh using settings. Mesh is generated in device space
|
||||||
/// coordinate system. You must transform the mesh, if you want to
|
/// coordinate system. You must transform the mesh, if you want to
|
||||||
|
Reference in New Issue
Block a user