Bugfixes - PostScript functions can have blocks without if - for example '{ 2 3 add }'

This commit is contained in:
Jakub Melka 2019-04-29 14:14:06 +02:00
parent a407dbd3f3
commit bfcc48ff1b
6 changed files with 148 additions and 35 deletions

View File

@ -85,38 +85,30 @@ HEADERS += \
sources/pdfnametounicode.h \
sources/pdffont.h
unix {
target.path = /usr/lib
INSTALLS += target
}
# Link to freetype library
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../FreeType/ -lfreetype
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../FreeType/ -lfreetype
else:unix: LIBS += -L$$PWD/FreeType/ -lfreetype
INCLUDEPATH += $$PWD/../FreeType/include
DEPENDPATH += $$PWD/../FreeType/include
freetype_lib.files = $$PWD/../FreeType/freetype.dll
freetype_lib.path = $$OUT_PWD
INSTALLS += freetype_lib
CONFIG += force_debug_info
QMAKE_CXXFLAGS += /std:c++latest /utf-8
QMAKE_RESOURCE_FLAGS += -threshold 0 -compress 9
message($$QMAKE_RESOURCE_FLAGS)
FORMS += \
sources/pdfrenderingerrorswidget.ui
RESOURCES += \
cmaps.qrc
# Link to freetype library
LIBS += -L$$PWD/../FreeType/ -lfreetype
INCLUDEPATH += $$PWD/../FreeType/include
DEPENDPATH += $$PWD/../FreeType/include
# Add freetype to installations
freetype_lib.files = $$PWD/../FreeType/freetype.dll
freetype_lib.path = $$DESTDIR
INSTALLS += freetype_lib
# ensure debug info even for RELEASE build
CONFIG += force_debug_info
QMAKE_CXXFLAGS += /std:c++latest /utf-8
# resource manifest
CMAP_RESOURCE_INPUT = $$PWD/cmaps.qrc
cmap_resource_builder.commands = $$[QT_HOST_BINS]/rcc -binary ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} -threshold 0 -compress 9
cmap_resource_builder.depend_command = $$[QT_HOST_BINS]/rcc -list $$QMAKE_RESOURCE_FLAGS ${QMAKE_FILE_IN}
cmap_resource_builder.input = CMAP_RESOURCE_INPUT
cmap_resource_builder.output = $$DESTDIR/${QMAKE_FILE_IN_BASE}.qrb
cmap_resource_builder.CONFIG += no_link target_predeps
QMAKE_EXTRA_COMPILERS += cmap_resource_builder

View File

@ -1246,6 +1246,14 @@ void PDFPostScriptFunctionExecutor::execute()
break;
}
case PDFPostScriptFunction::Code::Execute:
{
const PDFPostScriptFunctionStack::InstructionPointer callIp = m_stack.popInstructionPointer();
callStack.push(instruction.next);
ip = callIp;
continue;
}
case PDFPostScriptFunction::Code::If:
{
const PDFPostScriptFunctionStack::InstructionPointer callIp = m_stack.popInstructionPointer();
@ -1480,7 +1488,7 @@ PDFReal PDFPostScriptFunctionStack::popNumber()
}
else
{
throw PDFPostScriptFunction::PDFPostScriptFunctionException(PDFTranslationContext::tr("Instruction pointer expected (PostScript engine)."));
throw PDFPostScriptFunction::PDFPostScriptFunctionException(PDFTranslationContext::tr("Number expected (PostScript engine)."));
}
}
@ -1741,9 +1749,86 @@ PDFPostScriptFunction::Program PDFPostScriptFunction::parseProgram(const QByteAr
throw PDFParserException(PDFTranslationContext::tr("Empty program (PostScript function)."));
}
// Mark we are at the end of the program
result.back().next = INVALID_INSTRUCTION_POINTER;
// We must insert execute instructions, where blocks without if/ifelse occurs.
// We can have following program "{ 2 3 add }" which must return 5. How to find blocks,
// after which instructions must be executed? Next instruction must be if, or next instruction
// must be a call and next-next instruction must be ifelse
auto isBlockUsed = [&result](InstructionPointer ip)
{
// We should call this function only on Call opcode
Q_ASSERT(result[ip].code == Code::Call);
const InstructionPointer next = result[ip].next;
if (next < result.size())
{
switch (result[next].code)
{
case Code::If:
case Code::IfElse:
{
// Block is used in 'If' statement
return true;
}
case Code::Call:
{
// We must detect, if we use 'If-Else' statement
const InstructionPointer nextnext = result[next].next;
if (nextnext < result.size())
{
return result[nextnext].code == Code::IfElse;
}
return false;
}
default:
return false;
}
}
return false;
};
// Insert execute instructions, where there are call blocks, which are not used in if/ifelse statements
for (size_t i = 0; i < result.size(); ++i)
{
if (result[i].code == Code::Call && !isBlockUsed(i))
{
InstructionPointer insertPosition = result[i].next;
// We must update the instructions pointers for inserting the instruction
for (CodeObject& codeObject : result)
{
if (codeObject.next > insertPosition && codeObject.next != INVALID_INSTRUCTION_POINTER)
{
++codeObject.next;
}
if (codeObject.operand.type == OperandType::InstructionPointer &&
codeObject.operand.instructionPointer > insertPosition &&
codeObject.operand.instructionPointer != INVALID_INSTRUCTION_POINTER)
{
++codeObject.operand.instructionPointer;
}
}
// We must insert an execute statement, block is not used in if/ifelse statement
result.insert(std::next(result.begin(), insertPosition), CodeObject(Code::Execute, insertPosition + 1));
}
}
// Mark we are at the end of the program
for (CodeObject& codeObject : result)
{
if (codeObject.next == result.size())
{
codeObject.next = INVALID_INSTRUCTION_POINTER;
}
}
Q_ASSERT(result.back().next == INVALID_INSTRUCTION_POINTER);
result.shrink_to_fit();
return result;
}
@ -1781,7 +1866,7 @@ PDFFunction::FunctionResult PDFPostScriptFunction::apply(const_iterator x_1, con
auto itEnd = std::make_reverse_iterator(y_1);
for (; it != itEnd; ++it)
{
const PDFReal y = stack.popReal();
const PDFReal y = stack.popNumber();
const PDFReal yClamped = clampOutput(--i, y);
*it = yClamped;
}

View File

@ -403,7 +403,8 @@ public:
// blocks (call and return function).
Call,
Return,
Push
Push,
Execute
};
/// Gets the code from the byte array. If byte array contains invalid data,

View File

@ -1351,6 +1351,7 @@ void PDFPageContentProcessor::operatorColorSetStrokingColorSpace(PDFPageContentP
m_graphicState.setStrokeColorSpace(colorSpace);
m_graphicState.setStrokeColor(colorSpace->getDefaultColor());
updateGraphicState();
checkStrokingColor();
}
else
{
@ -1367,6 +1368,7 @@ void PDFPageContentProcessor::operatorColorSetFillingColorSpace(PDFOperandName n
m_graphicState.setFillColorSpace(colorSpace);
m_graphicState.setFillColor(colorSpace->getDefaultColor());
updateGraphicState();
checkFillingColor();
}
else
{
@ -1389,6 +1391,7 @@ void PDFPageContentProcessor::operatorColorSetStrokingColor()
}
m_graphicState.setStrokeColor(colorSpace->getColor(color));
updateGraphicState();
checkStrokingColor();
}
else
{
@ -1419,6 +1422,7 @@ void PDFPageContentProcessor::operatorColorSetFillingColor()
}
m_graphicState.setFillColor(colorSpace->getColor(color));
updateGraphicState();
checkFillingColor();
}
else
{
@ -1439,6 +1443,7 @@ void PDFPageContentProcessor::operatorColorSetDeviceGrayStroking(PDFReal gray)
m_graphicState.setStrokeColorSpace(m_deviceGrayColorSpace);
m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), gray));
updateGraphicState();
checkStrokingColor();
}
void PDFPageContentProcessor::operatorColorSetDeviceGrayFilling(PDFReal gray)
@ -1446,6 +1451,7 @@ void PDFPageContentProcessor::operatorColorSetDeviceGrayFilling(PDFReal gray)
m_graphicState.setFillColorSpace(m_deviceGrayColorSpace);
m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), gray));
updateGraphicState();
checkFillingColor();
}
void PDFPageContentProcessor::operatorColorSetDeviceRGBStroking(PDFReal r, PDFReal g, PDFReal b)
@ -1453,6 +1459,7 @@ void PDFPageContentProcessor::operatorColorSetDeviceRGBStroking(PDFReal r, PDFRe
m_graphicState.setStrokeColorSpace(m_deviceRGBColorSpace);
m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), r, g, b));
updateGraphicState();
checkStrokingColor();
}
void PDFPageContentProcessor::operatorColorSetDeviceRGBFilling(PDFReal r, PDFReal g, PDFReal b)
@ -1460,6 +1467,7 @@ void PDFPageContentProcessor::operatorColorSetDeviceRGBFilling(PDFReal r, PDFRea
m_graphicState.setFillColorSpace(m_deviceRGBColorSpace);
m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), r, g, b));
updateGraphicState();
checkFillingColor();
}
void PDFPageContentProcessor::operatorColorSetDeviceCMYKStroking(PDFReal c, PDFReal m, PDFReal y, PDFReal k)
@ -1467,6 +1475,7 @@ void PDFPageContentProcessor::operatorColorSetDeviceCMYKStroking(PDFReal c, PDFR
m_graphicState.setStrokeColorSpace(m_deviceCMYKColorSpace);
m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), c, m, y, k));
updateGraphicState();
checkStrokingColor();
}
void PDFPageContentProcessor::operatorColorSetDeviceCMYKFilling(PDFReal c, PDFReal m, PDFReal y, PDFReal k)
@ -1474,6 +1483,7 @@ void PDFPageContentProcessor::operatorColorSetDeviceCMYKFilling(PDFReal c, PDFRe
m_graphicState.setFillColorSpace(m_deviceCMYKColorSpace);
m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), c, m, y, k));
updateGraphicState();
checkFillingColor();
}
void PDFPageContentProcessor::operatorTextBegin()
@ -1833,6 +1843,22 @@ PDFRealizedFontPointer PDFPageContentProcessor::getRealizedFontImpl() const
return PDFRealizedFontPointer();
}
void PDFPageContentProcessor::checkStrokingColor()
{
if (!m_graphicState.getStrokeColor().isValid())
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid stroking color."));
}
}
void PDFPageContentProcessor::checkFillingColor()
{
if (!m_graphicState.getFillColor().isValid())
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid filling color."));
}
}
PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() :
m_currentTransformationMatrix(),
m_fillColorSpace(),

View File

@ -575,6 +575,12 @@ private:
/// Returns realized font (or empty font, if font can't be realized)
PDFRealizedFontPointer getRealizedFontImpl() const;
/// Checks, if stroking color is valid
void checkStrokingColor();
/// Checks, if filling color is valid
void checkFillingColor();
const PDFPage* m_page;
const PDFDocument* m_document;
const PDFFontCache* m_fontCache;

View File

@ -17,6 +17,7 @@
#include "pdfviewermainwindow.h"
#include <QResource>
#include <QApplication>
#include <QCommandLineParser>
@ -34,6 +35,8 @@ int main(int argc, char *argv[])
parser.addPositionalArgument("file", "The PDF file to open.");
parser.process(application);
QResource::registerResource(QString("cmaps.qrb"));
pdfviewer::PDFViewerMainWindow mainWindow;
mainWindow.show();