mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Bugfixes - PostScript functions can have blocks without if - for example '{ 2 3 add }'
This commit is contained in:
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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(),
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user