Bugfixing for patterns

This commit is contained in:
Jakub Melka
2019-09-13 16:28:20 +02:00
parent 6326eb35eb
commit 0d048fccbd
5 changed files with 186 additions and 66 deletions

View File

@ -350,12 +350,11 @@ PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictio
{ {
stream = colorSpaceSettings.getStream(); stream = colorSpaceSettings.getStream();
} }
}
if (name == COLOR_SPACE_NAME_PATTERN) if (name == COLOR_SPACE_NAME_PATTERN)
{ {
PDFPatternPtr pattern = PDFPattern::createPattern(colorSpaceDictionary, document, array->getItem(1)); return PDFColorSpacePointer(new PDFPatternColorSpace(std::make_shared<PDFInvalidPattern>()));
return PDFColorSpacePointer(new PDFPatternColorSpace(qMove(pattern)));
}
} }
if (dictionary) if (dictionary)
@ -414,6 +413,11 @@ PDFColorSpacePointer PDFAbstractColorSpace::createDeviceColorSpaceByNameImpl(con
throw PDFParserException(PDFTranslationContext::tr("Can't load color space, because color space structure is too complex.")); throw PDFParserException(PDFTranslationContext::tr("Can't load color space, because color space structure is too complex."));
} }
if (name == COLOR_SPACE_NAME_PATTERN)
{
return PDFColorSpacePointer(new PDFPatternColorSpace(std::make_shared<PDFInvalidPattern>()));
}
if (name == COLOR_SPACE_NAME_DEVICE_GRAY || name == COLOR_SPACE_NAME_ABBREVIATION_DEVICE_GRAY) if (name == COLOR_SPACE_NAME_DEVICE_GRAY || name == COLOR_SPACE_NAME_ABBREVIATION_DEVICE_GRAY)
{ {
if (colorSpaceDictionary && colorSpaceDictionary->hasKey(COLOR_SPACE_NAME_DEFAULT_GRAY)) if (colorSpaceDictionary && colorSpaceDictionary->hasKey(COLOR_SPACE_NAME_DEFAULT_GRAY))
@ -985,7 +989,7 @@ const unsigned char* PDFImageData::getRow(unsigned int rowIndex) const
QColor PDFPatternColorSpace::getDefaultColor() const QColor PDFPatternColorSpace::getDefaultColor() const
{ {
throw PDFParserException(PDFTranslationContext::tr("Pattern doesn't have default color.")); return QColor(Qt::transparent);
} }
QColor PDFPatternColorSpace::getColor(const PDFColor& color) const QColor PDFPatternColorSpace::getColor(const PDFColor& color) const

View File

@ -177,6 +177,7 @@ void PDFPageContentProcessor::initDictionaries(const PDFObject& resourcesObject)
m_extendedGraphicStateDictionary = getDictionary(PDF_RESOURCE_EXTGSTATE); m_extendedGraphicStateDictionary = getDictionary(PDF_RESOURCE_EXTGSTATE);
m_propertiesDictionary = getDictionary("Properties"); m_propertiesDictionary = getDictionary("Properties");
m_shadingDictionary = getDictionary("Shading"); m_shadingDictionary = getDictionary("Shading");
m_patternDictionary = getDictionary("Pattern");
} }
PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
@ -194,6 +195,7 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
m_extendedGraphicStateDictionary(nullptr), m_extendedGraphicStateDictionary(nullptr),
m_propertiesDictionary(nullptr), m_propertiesDictionary(nullptr),
m_shadingDictionary(nullptr), m_shadingDictionary(nullptr),
m_patternDictionary(nullptr),
m_textBeginEndState(0), m_textBeginEndState(0),
m_compatibilityBeginEndState(0), m_compatibilityBeginEndState(0),
m_patternBaseMatrix(pagePointToDevicePointMatrix), m_patternBaseMatrix(pagePointToDevicePointMatrix),
@ -595,27 +597,47 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
const PDFPattern* pattern = getGraphicState()->getFillColorSpace()->getPattern(); const PDFPattern* pattern = getGraphicState()->getFillColorSpace()->getPattern();
if (pattern) if (pattern)
{ {
if (const PDFShadingPattern* shadingPatern = pattern->getShadingPattern()) switch (pattern->getType())
{ {
// We must create a mesh and then draw pattern case PatternType::Tiling:
PDFMeshQualitySettings settings; {
settings.deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace(); // TODO: Implement tiling pattern
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix(); throw PDFParserException(PDFTranslationContext::tr("Tiling pattern not implemented."));
settings.initDefaultResolution(); break;
}
PDFMesh mesh = shadingPatern->createMesh(settings); case PatternType::Shading:
{
const PDFShadingPattern* shadingPatern = pattern->getShadingPattern();
// Now, merge the current path to the mesh clipping path // We must create a mesh and then draw pattern
QPainterPath boundingPath = mesh.getBoundingPath(); PDFMeshQualitySettings settings;
boundingPath.addPath(getCurrentWorldMatrix().map(path)); settings.deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace();
mesh.setBoundingPath(boundingPath); settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
settings.initDefaultResolution();
performMeshPainting(mesh); PDFMesh mesh = shadingPatern->createMesh(settings);
}
else // Now, merge the current path to the mesh clipping path
{ QPainterPath boundingPath = mesh.getBoundingPath();
// TODO: Implement tiling pattern boundingPath.addPath(getCurrentWorldMatrix().map(path));
Q_ASSERT(false); mesh.setBoundingPath(boundingPath);
performMeshPainting(mesh);
break;
}
case PatternType::Invalid:
{
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
break;
}
default:
{
Q_ASSERT(false);
break;
}
} }
fill = false; fill = false;
@ -627,41 +649,61 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
const PDFPattern* pattern = getGraphicState()->getStrokeColorSpace()->getPattern(); const PDFPattern* pattern = getGraphicState()->getStrokeColorSpace()->getPattern();
if (pattern) if (pattern)
{ {
if (const PDFShadingPattern* shadingPatern = pattern->getShadingPattern()) switch (pattern->getType())
{ {
// We must create a mesh and then draw pattern case PatternType::Tiling:
PDFMeshQualitySettings settings;
settings.deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace();
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
settings.initDefaultResolution();
PDFMesh mesh = shadingPatern->createMesh(settings);
// 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())); // TODO: Implement tiling pattern
stroker.setDashOffset(lineDashPattern.getDashOffset()); throw PDFParserException(PDFTranslationContext::tr("Tiling pattern not implemented."));
break;
} }
QPainterPath strokedPath = stroker.createStroke(path);
QPainterPath boundingPath = mesh.getBoundingPath(); case PatternType::Shading:
boundingPath.addPath(getCurrentWorldMatrix().map(strokedPath)); {
mesh.setBoundingPath(boundingPath); const PDFShadingPattern* shadingPatern = pattern->getShadingPattern();
performMeshPainting(mesh); // We must create a mesh and then draw pattern
} PDFMeshQualitySettings settings;
else settings.deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace();
{ settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
// TODO: Implement tiling pattern settings.initDefaultResolution();
Q_ASSERT(false);
PDFMesh mesh = shadingPatern->createMesh(settings);
// 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);
QPainterPath boundingPath = mesh.getBoundingPath();
boundingPath.addPath(getCurrentWorldMatrix().map(strokedPath));
mesh.setBoundingPath(boundingPath);
performMeshPainting(mesh);
break;
}
case PatternType::Invalid:
{
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
break;
}
default:
{
Q_ASSERT(false);
break;
}
} }
stroke = false; stroke = false;
@ -1807,8 +1849,35 @@ void PDFPageContentProcessor::operatorColorSetStrokingColorN()
{ {
// In our implementation, operator 'SC' can also set color using all color spaces // In our implementation, operator 'SC' can also set color using all color spaces
// PDF reference 1.7 allows here Pattern, Separation, DeviceN and ICCBased color spaces here, // PDF reference 1.7 allows here Pattern, Separation, DeviceN and ICCBased color spaces here,
// but default operator can use them (with exception of Pattern color space). // but default operator can use them (with exception of Pattern color space). For pattern color space,
operatorColorSetStrokingColor(); // we treat this differently.
const PDFAbstractColorSpace* colorSpace = m_graphicState.getStrokeColorSpace();
if (colorSpace->getPattern())
{
if (m_operands.size() > 0)
{
// TODO: Implement tiling pattern colors
PDFOperandName name = readOperand<PDFOperandName>(m_operands.size() - 1);
if (m_patternDictionary && m_patternDictionary->hasKey(name.name))
{
// Create the pattern
PDFPatternPtr pattern = PDFPattern::createPattern(m_colorSpaceDictionary, m_document, m_patternDictionary->get(name.name));
m_graphicState.setStrokeColorSpace(QSharedPointer<PDFAbstractColorSpace>(new PDFPatternColorSpace(qMove(pattern))));
updateGraphicState();
return;
}
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid pattern for Pattern color space."));
}
else
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid pattern for Pattern color space."));
}
}
else
{
operatorColorSetStrokingColor();
}
} }
void PDFPageContentProcessor::operatorColorSetFillingColor() void PDFPageContentProcessor::operatorColorSetFillingColor()
@ -1838,8 +1907,35 @@ void PDFPageContentProcessor::operatorColorSetFillingColorN()
{ {
// In our implementation, operator 'sc' can also set color using all color spaces // In our implementation, operator 'sc' can also set color using all color spaces
// PDF reference 1.7 allows here Pattern, Separation, DeviceN and ICCBased color spaces here, // PDF reference 1.7 allows here Pattern, Separation, DeviceN and ICCBased color spaces here,
// but default operator can use them (with exception of Pattern color space). // but default operator can use them (with exception of Pattern color space). For pattern color space,
operatorColorSetFillingColor(); // we treat this differently.
const PDFAbstractColorSpace* colorSpace = m_graphicState.getFillColorSpace();
if (colorSpace->getPattern())
{
if (m_operands.size() > 0)
{
// TODO: Implement tiling pattern colors
PDFOperandName name = readOperand<PDFOperandName>(m_operands.size() - 1);
if (m_patternDictionary && m_patternDictionary->hasKey(name.name))
{
// Create the pattern
PDFPatternPtr pattern = PDFPattern::createPattern(m_colorSpaceDictionary, m_document, m_patternDictionary->get(name.name));
m_graphicState.setFillColorSpace(QSharedPointer<PDFAbstractColorSpace>(new PDFPatternColorSpace(qMove(pattern))));
updateGraphicState();
return;
}
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid pattern for Pattern color space."));
}
else
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid pattern for Pattern color space."));
}
}
else
{
operatorColorSetFillingColor();
}
} }
void PDFPageContentProcessor::operatorColorSetDeviceGrayStroking(PDFReal gray) void PDFPageContentProcessor::operatorColorSetDeviceGrayStroking(PDFReal gray)
@ -2864,7 +2960,8 @@ PDFPageContentProcessor::PDFPageContentProcessorStateGuard::PDFPageContentProces
m_xobjectDictionary(processor->m_xobjectDictionary), m_xobjectDictionary(processor->m_xobjectDictionary),
m_extendedGraphicStateDictionary(processor->m_extendedGraphicStateDictionary), m_extendedGraphicStateDictionary(processor->m_extendedGraphicStateDictionary),
m_propertiesDictionary(processor->m_propertiesDictionary), m_propertiesDictionary(processor->m_propertiesDictionary),
m_shadingDictionary(processor->m_shadingDictionary) m_shadingDictionary(processor->m_shadingDictionary),
m_patternDictionary(processor->m_patternDictionary)
{ {
m_processor->operatorSaveGraphicState(); m_processor->operatorSaveGraphicState();
} }
@ -2878,6 +2975,7 @@ PDFPageContentProcessor::PDFPageContentProcessorStateGuard::~PDFPageContentProce
m_processor->m_extendedGraphicStateDictionary = m_extendedGraphicStateDictionary; m_processor->m_extendedGraphicStateDictionary = m_extendedGraphicStateDictionary;
m_processor->m_propertiesDictionary = m_propertiesDictionary; m_processor->m_propertiesDictionary = m_propertiesDictionary;
m_processor->m_shadingDictionary = m_shadingDictionary; m_processor->m_shadingDictionary = m_shadingDictionary;
m_processor->m_patternDictionary = m_patternDictionary;
m_processor->operatorRestoreGraphicState(); m_processor->operatorRestoreGraphicState();
} }

View File

@ -502,6 +502,7 @@ private:
const PDFDictionary* m_extendedGraphicStateDictionary; const PDFDictionary* m_extendedGraphicStateDictionary;
const PDFDictionary* m_propertiesDictionary; const PDFDictionary* m_propertiesDictionary;
const PDFDictionary* m_shadingDictionary; const PDFDictionary* m_shadingDictionary;
const PDFDictionary* m_patternDictionary;
}; };
/// Wrapper for PDF Name /// Wrapper for PDF Name
@ -724,6 +725,7 @@ private:
const PDFDictionary* m_extendedGraphicStateDictionary; const PDFDictionary* m_extendedGraphicStateDictionary;
const PDFDictionary* m_propertiesDictionary; const PDFDictionary* m_propertiesDictionary;
const PDFDictionary* m_shadingDictionary; const PDFDictionary* m_shadingDictionary;
const PDFDictionary* m_patternDictionary;
// Default color spaces // Default color spaces
PDFColorSpacePointer m_deviceGrayColorSpace; PDFColorSpacePointer m_deviceGrayColorSpace;

View File

@ -48,11 +48,6 @@ PDFPatternPtr PDFPattern::createPattern(const PDFDictionary* colorSpaceDictionar
const PDFDictionary* patternDictionary = dereferencedObject.getDictionary(); const PDFDictionary* patternDictionary = dereferencedObject.getDictionary();
PDFDocumentDataLoaderDecorator loader(document); PDFDocumentDataLoaderDecorator loader(document);
if (loader.readNameFromDictionary(patternDictionary, "Type") != "Pattern")
{
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
}
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)));
switch (patternType) switch (patternType)
{ {
@ -89,16 +84,27 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
bool ignoreBackgroundColor) bool ignoreBackgroundColor)
{ {
const PDFObject& dereferencedShadingObject = document->getObject(shadingObject); const PDFObject& dereferencedShadingObject = document->getObject(shadingObject);
if (!dereferencedShadingObject.isDictionary()) if (!dereferencedShadingObject.isDictionary() && !dereferencedShadingObject.isStream())
{ {
throw PDFParserException(PDFTranslationContext::tr("Invalid shading.")); throw PDFParserException(PDFTranslationContext::tr("Invalid shading."));
} }
PDFDocumentDataLoaderDecorator loader(document); PDFDocumentDataLoaderDecorator loader(document);
const PDFDictionary* shadingDictionary = dereferencedShadingObject.getDictionary(); const PDFDictionary* shadingDictionary = nullptr;
const PDFStream* stream = nullptr;
if (dereferencedShadingObject.isDictionary())
{
shadingDictionary = dereferencedShadingObject.getDictionary();
}
else if (dereferencedShadingObject.isStream())
{
stream = dereferencedShadingObject.getStream();
shadingDictionary = stream->getDictionary();
}
// Parse common data for all shadings // Parse common data for all shadings
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(colorSpaceDictionary, document, shadingDictionary->get("ColorSpace")); PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(colorSpaceDictionary, document, document->getObject(shadingDictionary->get("ColorSpace")));
if (colorSpace->getPattern()) if (colorSpace->getPattern())
{ {
@ -1183,5 +1189,6 @@ PDFMesh PDFRadialShading::createMesh(const PDFMeshQualitySettings& settings) con
} }
// TODO: Apply graphic state of the pattern // TODO: Apply graphic state of the pattern
// TODO: Implement settings of meshing in the settings dialog
} // namespace pdf } // namespace pdf

View File

@ -204,6 +204,15 @@ protected:
QMatrix m_matrix; QMatrix m_matrix;
}; };
class PDFInvalidPattern : public PDFPattern
{
public:
explicit PDFInvalidPattern() = default;
virtual PatternType getType() const { return PatternType::Invalid; }
virtual const PDFShadingPattern* getShadingPattern() const { return nullptr; }
};
/// Shading pattern - smooth color distribution along the pattern's space /// Shading pattern - smooth color distribution along the pattern's space
class PDFShadingPattern : public PDFPattern class PDFShadingPattern : public PDFPattern
{ {