mirror of https://github.com/JakubMelka/PDF4QT.git
Bugfixes - PostScript functions can have blocks without if - for example '{ 2 3 add }'
This commit is contained in:
parent
a407dbd3f3
commit
bfcc48ff1b
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue