mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-01-29 16:49:32 +01:00
Tiling patterns (first part)
This commit is contained in:
parent
d87995f8b8
commit
40f3f9f9b4
@ -601,14 +601,14 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
||||
{
|
||||
case PatternType::Tiling:
|
||||
{
|
||||
// TODO: Implement tiling pattern
|
||||
throw PDFParserException(PDFTranslationContext::tr("Tiling pattern not implemented."));
|
||||
const PDFTilingPattern* tilingPattern = pattern->getTilingPattern();
|
||||
processTillingPatternPainting(tilingPattern, path);
|
||||
break;
|
||||
}
|
||||
|
||||
case PatternType::Shading:
|
||||
{
|
||||
const PDFShadingPattern* shadingPatern = pattern->getShadingPattern();
|
||||
const PDFShadingPattern* shadingPattern = pattern->getShadingPattern();
|
||||
|
||||
// We must create a mesh and then draw pattern
|
||||
PDFMeshQualitySettings settings;
|
||||
@ -616,7 +616,7 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
||||
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
|
||||
settings.initDefaultResolution();
|
||||
|
||||
PDFMesh mesh = shadingPatern->createMesh(settings);
|
||||
PDFMesh mesh = shadingPattern->createMesh(settings);
|
||||
|
||||
// Now, merge the current path to the mesh clipping path
|
||||
QPainterPath boundingPath = mesh.getBoundingPath();
|
||||
@ -660,8 +660,23 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
||||
{
|
||||
case PatternType::Tiling:
|
||||
{
|
||||
// TODO: Implement tiling pattern
|
||||
throw PDFParserException(PDFTranslationContext::tr("Tiling pattern not implemented."));
|
||||
const PDFTilingPattern* tilingPattern = pattern->getTilingPattern();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
Operator op = Operator::Invalid;
|
||||
|
@ -35,6 +35,7 @@
|
||||
namespace pdf
|
||||
{
|
||||
class PDFMesh;
|
||||
class PDFTilingPattern;
|
||||
class PDFOptionalContentActivity;
|
||||
|
||||
static constexpr const char* PDF_RESOURCE_EXTGSTATE = "ExtGState";
|
||||
@ -464,6 +465,11 @@ private:
|
||||
/// \param fillRule Fill rule used in the fill mode
|
||||
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
|
||||
{
|
||||
OptionalContent,
|
||||
@ -505,6 +511,23 @@ private:
|
||||
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
|
||||
struct PDFOperandName
|
||||
{
|
||||
|
@ -46,11 +46,22 @@ ShadingType PDFAxialShading::getShadingType() const
|
||||
PDFPatternPtr PDFPattern::createPattern(const PDFDictionary* colorSpaceDictionary, const PDFDocument* document, const PDFObject& object)
|
||||
{
|
||||
const PDFObject& dereferencedObject = document->getObject(object);
|
||||
const PDFDictionary* patternDictionary = nullptr;
|
||||
QByteArray streamData;
|
||||
|
||||
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);
|
||||
|
||||
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:
|
||||
{
|
||||
// TODO: Implement tiling pattern
|
||||
throw PDFParserException(PDFTranslationContext::tr("Tiling pattern not implemented."));
|
||||
break;
|
||||
const PDFTilingPattern::PaintType paintType = static_cast<PDFTilingPattern::PaintType>(loader.readIntegerFromDictionary(patternDictionary, "PaintType", static_cast<PDFInteger>(PDFTilingPattern::PaintType::Invalid)));
|
||||
const PDFTilingPattern::TilingType tilingType = static_cast<PDFTilingPattern::TilingType>(loader.readIntegerFromDictionary(patternDictionary, "TilingType", static_cast<PDFInteger>(PDFTilingPattern::TilingType::Invalid)));
|
||||
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:
|
||||
@ -74,7 +119,7 @@ PDFPatternPtr PDFPattern::createPattern(const PDFDictionary* colorSpaceDictionar
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
|
||||
}
|
||||
|
||||
return result;
|
||||
return PDFPatternPtr();
|
||||
}
|
||||
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
|
||||
|
@ -29,6 +29,7 @@
|
||||
namespace pdf
|
||||
{
|
||||
class PDFPattern;
|
||||
class PDFTilingPattern;
|
||||
class PDFShadingPattern;
|
||||
|
||||
using PDFPatternPtr = std::shared_ptr<PDFPattern>;
|
||||
@ -193,6 +194,7 @@ public:
|
||||
|
||||
virtual PatternType getType() 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
|
||||
/// pattern coordinate system).
|
||||
@ -233,7 +235,50 @@ public:
|
||||
explicit PDFInvalidPattern() = default;
|
||||
|
||||
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
|
||||
@ -245,6 +290,7 @@ public:
|
||||
virtual PatternType getType() const override;
|
||||
virtual ShadingType getShadingType() const = 0;
|
||||
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
|
||||
/// coordinate system. You must transform the mesh, if you want to
|
||||
|
Loading…
x
Reference in New Issue
Block a user