Parsing of PostScript function

This commit is contained in:
Jakub Melka 2019-03-13 19:05:21 +01:00
parent ea9b513160
commit 0f0cd575d4
2 changed files with 162 additions and 1 deletions

View File

@ -1556,4 +1556,154 @@ void PDFPostScriptFunctionStack::checkUnderflow(size_t n) const
}
}
PDFPostScriptFunction::Code PDFPostScriptFunction::getCode(const QByteArray& byteArray)
{
static constexpr const std::pair<Code, const char*> codes[] =
{
// B.1 Arithmetic operators
std::pair<Code, const char*>{ Code::Add, "add" },
std::pair<Code, const char*>{ Code::Sub, "sub" },
std::pair<Code, const char*>{ Code::Mul, "mul" },
std::pair<Code, const char*>{ Code::Div, "div" },
std::pair<Code, const char*>{ Code::Idiv, "idiv" },
std::pair<Code, const char*>{ Code::Mod, "mod" },
std::pair<Code, const char*>{ Code::Neg, "neg" },
std::pair<Code, const char*>{ Code::Abs, "abs" },
std::pair<Code, const char*>{ Code::Ceiling, "ceiling" },
std::pair<Code, const char*>{ Code::Floor, "floor" },
std::pair<Code, const char*>{ Code::Round, "round" },
std::pair<Code, const char*>{ Code::Truncate, "truncate" },
std::pair<Code, const char*>{ Code::Sqrt, "sqrt" },
std::pair<Code, const char*>{ Code::Sin, "sin" },
std::pair<Code, const char*>{ Code::Cos, "cos" },
std::pair<Code, const char*>{ Code::Atan, "atan" },
std::pair<Code, const char*>{ Code::Exp, "exp" },
std::pair<Code, const char*>{ Code::Ln, "ln" },
std::pair<Code, const char*>{ Code::Log, "log" },
std::pair<Code, const char*>{ Code::Cvi, "cvi" },
std::pair<Code, const char*>{ Code::Cvr, "cvr" },
// B.2 Relational, Boolean and Bitwise operators
std::pair<Code, const char*>{ Code::Eq, "eq" },
std::pair<Code, const char*>{ Code::Ne, "ne" },
std::pair<Code, const char*>{ Code::Gt, "gt" },
std::pair<Code, const char*>{ Code::Ge, "ge" },
std::pair<Code, const char*>{ Code::Lt, "lt" },
std::pair<Code, const char*>{ Code::Le, "le" },
std::pair<Code, const char*>{ Code::And, "and" },
std::pair<Code, const char*>{ Code::Or, "or" },
std::pair<Code, const char*>{ Code::Xor, "xor" },
std::pair<Code, const char*>{ Code::Not, "not" },
std::pair<Code, const char*>{ Code::Bitshift, "bitshift" },
std::pair<Code, const char*>{ Code::True, "true" },
std::pair<Code, const char*>{ Code::False, "false" },
// B.3 Conditional operators
std::pair<Code, const char*>{ Code::If, "if" },
std::pair<Code, const char*>{ Code::IfElse, "ifelse" },
// B.4 Stack operators
std::pair<Code, const char*>{ Code::Pop, "pop" },
std::pair<Code, const char*>{ Code::Exch, "exch" },
std::pair<Code, const char*>{ Code::Dup, "dup" },
std::pair<Code, const char*>{ Code::Copy, "copy" },
std::pair<Code, const char*>{ Code::Index, "index" },
std::pair<Code, const char*>{ Code::Roll, "roll" }
};
for (const std::pair<Code, const char*>& codeItem : codes)
{
if (byteArray == codeItem.second)
{
return codeItem.first;
}
}
throw PDFParserException(PDFTranslationContext::tr("Invalid operator (PostScript function) '%1'.").arg(QString::fromLatin1(byteArray)));
}
PDFPostScriptFunction::Program PDFPostScriptFunction::parseProgram(const QByteArray& byteArray)
{
Program result;
PDFLexicalAnalyzer parser(byteArray.constBegin(), byteArray.constEnd());
std::stack<InstructionPointer> blockCallStack;
while (true)
{
PDFLexicalAnalyzer::Token token = parser.fetch();
if (token.type == PDFLexicalAnalyzer::TokenType::EndOfFile)
{
// We are at end, stop the parsing
break;
}
switch (token.type)
{
case PDFLexicalAnalyzer::TokenType::Boolean:
{
result.emplace_back(OperandObject::createBoolean(token.data.toBool()), result.size() + 1);
break;
}
case PDFLexicalAnalyzer::TokenType::Integer:
{
result.emplace_back(OperandObject::createInteger(token.data.toLongLong()), result.size() + 1);
break;
}
case PDFLexicalAnalyzer::TokenType::Real:
{
result.emplace_back(OperandObject::createInteger(token.data.toDouble()), result.size() + 1);
break;
}
case PDFLexicalAnalyzer::TokenType::Command:
{
QByteArray command = token.data.toByteArray();
if (command == "{")
{
// Opening bracket - means start of block
blockCallStack.push(result.size());
result.emplace_back(Code::Call, INVALID_INSTRUCTION_POINTER);
result.back().operand = OperandObject::createInstructionPointer(result.size());
}
else if (command == "}")
{
// Closing bracket - means end of block
if (blockCallStack.empty())
{
throw PDFParserException(PDFTranslationContext::tr("Invalid program - bad enclosing brackets (PostScript function)."));
}
result[blockCallStack.top()].next = result.size() + 1;
blockCallStack.pop();
result.emplace_back(Code::Return, INVALID_INSTRUCTION_POINTER);
}
else
{
result.emplace_back(getCode(command), result.size() + 1);
}
break;
}
default:
{
// All other tokens treat as invalid.
throw PDFParserException(PDFTranslationContext::tr("Invalid program (PostScript function)."));
}
}
}
if (result.empty())
{
throw PDFParserException(PDFTranslationContext::tr("Empty program (PostScript function)."));
}
// Mark we are at the end of the program
result.back().next = INVALID_INSTRUCTION_POINTER;
return result;
}
} // namespace pdf

View File

@ -321,7 +321,6 @@ class PDFFORQTLIBSHARED_EXPORT PDFPostScriptFunction : public PDFFunction
{
public:
private:
class PDFPostScriptFunctionException : public std::exception
{
public:
@ -407,6 +406,11 @@ private:
Push
};
/// Gets the code from the byte array. If byte array contains invalid data,
/// then exception is thrown.
/// \param byteArray Byte array to be converted to the code
static Code getCode(const QByteArray& byteArray);
struct OperandObject
{
explicit inline constexpr OperandObject() :
@ -437,6 +441,8 @@ private:
struct CodeObject
{
explicit inline CodeObject() : code(Code::Return), next(INVALID_INSTRUCTION_POINTER), operand() { }
explicit inline CodeObject(OperandObject operand, InstructionPointer next) : code(Code::Push), next(next), operand(std::move(operand)) { }
explicit inline CodeObject(Code code, InstructionPointer next) : code(code), next(next), operand() { }
Code code;
InstructionPointer next;
@ -445,6 +451,11 @@ private:
using Program = std::vector<CodeObject>;
static Program parseProgram(const QByteArray& byteArray);
private:
Program m_program;
friend class PDFPostScriptFunctionStack;
friend class PDFPostScriptFunctionExecutor;
};