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/pdfnametounicode.h \
sources/pdffont.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 += \ FORMS += \
sources/pdfrenderingerrorswidget.ui sources/pdfrenderingerrorswidget.ui
RESOURCES += \ # Link to freetype library
cmaps.qrc 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; break;
} }
case PDFPostScriptFunction::Code::Execute:
{
const PDFPostScriptFunctionStack::InstructionPointer callIp = m_stack.popInstructionPointer();
callStack.push(instruction.next);
ip = callIp;
continue;
}
case PDFPostScriptFunction::Code::If: case PDFPostScriptFunction::Code::If:
{ {
const PDFPostScriptFunctionStack::InstructionPointer callIp = m_stack.popInstructionPointer(); const PDFPostScriptFunctionStack::InstructionPointer callIp = m_stack.popInstructionPointer();
@ -1480,7 +1488,7 @@ PDFReal PDFPostScriptFunctionStack::popNumber()
} }
else 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).")); throw PDFParserException(PDFTranslationContext::tr("Empty program (PostScript function)."));
} }
// Mark we are at the end of the program // We must insert execute instructions, where blocks without if/ifelse occurs.
result.back().next = INVALID_INSTRUCTION_POINTER; // 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; return result;
} }
@ -1781,7 +1866,7 @@ PDFFunction::FunctionResult PDFPostScriptFunction::apply(const_iterator x_1, con
auto itEnd = std::make_reverse_iterator(y_1); auto itEnd = std::make_reverse_iterator(y_1);
for (; it != itEnd; ++it) for (; it != itEnd; ++it)
{ {
const PDFReal y = stack.popReal(); const PDFReal y = stack.popNumber();
const PDFReal yClamped = clampOutput(--i, y); const PDFReal yClamped = clampOutput(--i, y);
*it = yClamped; *it = yClamped;
} }

View File

@ -403,7 +403,8 @@ public:
// blocks (call and return function). // blocks (call and return function).
Call, Call,
Return, Return,
Push Push,
Execute
}; };
/// Gets the code from the byte array. If byte array contains invalid data, /// 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.setStrokeColorSpace(colorSpace);
m_graphicState.setStrokeColor(colorSpace->getDefaultColor()); m_graphicState.setStrokeColor(colorSpace->getDefaultColor());
updateGraphicState(); updateGraphicState();
checkStrokingColor();
} }
else else
{ {
@ -1367,6 +1368,7 @@ void PDFPageContentProcessor::operatorColorSetFillingColorSpace(PDFOperandName n
m_graphicState.setFillColorSpace(colorSpace); m_graphicState.setFillColorSpace(colorSpace);
m_graphicState.setFillColor(colorSpace->getDefaultColor()); m_graphicState.setFillColor(colorSpace->getDefaultColor());
updateGraphicState(); updateGraphicState();
checkFillingColor();
} }
else else
{ {
@ -1389,6 +1391,7 @@ void PDFPageContentProcessor::operatorColorSetStrokingColor()
} }
m_graphicState.setStrokeColor(colorSpace->getColor(color)); m_graphicState.setStrokeColor(colorSpace->getColor(color));
updateGraphicState(); updateGraphicState();
checkStrokingColor();
} }
else else
{ {
@ -1419,6 +1422,7 @@ void PDFPageContentProcessor::operatorColorSetFillingColor()
} }
m_graphicState.setFillColor(colorSpace->getColor(color)); m_graphicState.setFillColor(colorSpace->getColor(color));
updateGraphicState(); updateGraphicState();
checkFillingColor();
} }
else else
{ {
@ -1439,6 +1443,7 @@ void PDFPageContentProcessor::operatorColorSetDeviceGrayStroking(PDFReal gray)
m_graphicState.setStrokeColorSpace(m_deviceGrayColorSpace); m_graphicState.setStrokeColorSpace(m_deviceGrayColorSpace);
m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), gray)); m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), gray));
updateGraphicState(); updateGraphicState();
checkStrokingColor();
} }
void PDFPageContentProcessor::operatorColorSetDeviceGrayFilling(PDFReal gray) void PDFPageContentProcessor::operatorColorSetDeviceGrayFilling(PDFReal gray)
@ -1446,6 +1451,7 @@ void PDFPageContentProcessor::operatorColorSetDeviceGrayFilling(PDFReal gray)
m_graphicState.setFillColorSpace(m_deviceGrayColorSpace); m_graphicState.setFillColorSpace(m_deviceGrayColorSpace);
m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), gray)); m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), gray));
updateGraphicState(); updateGraphicState();
checkFillingColor();
} }
void PDFPageContentProcessor::operatorColorSetDeviceRGBStroking(PDFReal r, PDFReal g, PDFReal b) 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.setStrokeColorSpace(m_deviceRGBColorSpace);
m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), r, g, b)); m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), r, g, b));
updateGraphicState(); updateGraphicState();
checkStrokingColor();
} }
void PDFPageContentProcessor::operatorColorSetDeviceRGBFilling(PDFReal r, PDFReal g, PDFReal b) 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.setFillColorSpace(m_deviceRGBColorSpace);
m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), r, g, b)); m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), r, g, b));
updateGraphicState(); updateGraphicState();
checkFillingColor();
} }
void PDFPageContentProcessor::operatorColorSetDeviceCMYKStroking(PDFReal c, PDFReal m, PDFReal y, PDFReal k) 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.setStrokeColorSpace(m_deviceCMYKColorSpace);
m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), c, m, y, k)); m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), c, m, y, k));
updateGraphicState(); updateGraphicState();
checkStrokingColor();
} }
void PDFPageContentProcessor::operatorColorSetDeviceCMYKFilling(PDFReal c, PDFReal m, PDFReal y, PDFReal k) 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.setFillColorSpace(m_deviceCMYKColorSpace);
m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), c, m, y, k)); m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), c, m, y, k));
updateGraphicState(); updateGraphicState();
checkFillingColor();
} }
void PDFPageContentProcessor::operatorTextBegin() void PDFPageContentProcessor::operatorTextBegin()
@ -1833,6 +1843,22 @@ PDFRealizedFontPointer PDFPageContentProcessor::getRealizedFontImpl() const
return PDFRealizedFontPointer(); 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() : PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() :
m_currentTransformationMatrix(), m_currentTransformationMatrix(),
m_fillColorSpace(), m_fillColorSpace(),

View File

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

View File

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