mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Text operators (first part)
This commit is contained in:
@ -53,7 +53,9 @@ SOURCES += \
|
|||||||
sources/pdfpagecontentprocessor.cpp \
|
sources/pdfpagecontentprocessor.cpp \
|
||||||
sources/pdfpainter.cpp \
|
sources/pdfpainter.cpp \
|
||||||
sources/pdfrenderingerrorswidget.cpp \
|
sources/pdfrenderingerrorswidget.cpp \
|
||||||
sources/pdffunction.cpp
|
sources/pdffunction.cpp \
|
||||||
|
sources/pdfnametounicode.cpp \
|
||||||
|
sources/pdffont.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
sources/pdfobject.h \
|
sources/pdfobject.h \
|
||||||
@ -79,7 +81,9 @@ HEADERS += \
|
|||||||
sources/pdfpainter.h \
|
sources/pdfpainter.h \
|
||||||
sources/pdfutils.h \
|
sources/pdfutils.h \
|
||||||
sources/pdfrenderingerrorswidget.h \
|
sources/pdfrenderingerrorswidget.h \
|
||||||
sources/pdffunction.h
|
sources/pdffunction.h \
|
||||||
|
sources/pdfnametounicode.h \
|
||||||
|
sources/pdffont.h
|
||||||
|
|
||||||
unix {
|
unix {
|
||||||
target.path = /usr/lib
|
target.path = /usr/lib
|
||||||
|
@ -288,6 +288,18 @@ PDFReal PDFDocumentDataLoaderDecorator::readNumber(const PDFObject& object, PDFR
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PDFDocumentDataLoaderDecorator::readBoolean(const PDFObject& object, bool defaultValue) const
|
||||||
|
{
|
||||||
|
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||||
|
|
||||||
|
if (dereferencedObject.isBool())
|
||||||
|
{
|
||||||
|
return dereferencedObject.getBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
QString PDFDocumentDataLoaderDecorator::readTextString(const PDFObject& object, const QString& defaultValue) const
|
QString PDFDocumentDataLoaderDecorator::readTextString(const PDFObject& object, const QString& defaultValue) const
|
||||||
{
|
{
|
||||||
const PDFObject& dereferencedObject = m_document->getObject(object);
|
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||||
@ -436,4 +448,14 @@ std::vector<PDFInteger> PDFDocumentDataLoaderDecorator::readIntegerArray(const P
|
|||||||
return std::vector<PDFInteger>();
|
return std::vector<PDFInteger>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PDFDocumentDataLoaderDecorator::readBooleanFromDictionary(const PDFDictionary* dictionary, const char* key, bool defaultValue) const
|
||||||
|
{
|
||||||
|
if (dictionary->hasKey(key))
|
||||||
|
{
|
||||||
|
return readBoolean(dictionary->get(key), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -98,6 +98,11 @@ public:
|
|||||||
/// \param defaultValue Default value
|
/// \param defaultValue Default value
|
||||||
PDFReal readNumber(const PDFObject& object, PDFReal defaultValue) const;
|
PDFReal readNumber(const PDFObject& object, PDFReal defaultValue) const;
|
||||||
|
|
||||||
|
/// Reads a boolean from the object, if it is possible.
|
||||||
|
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
||||||
|
/// \param defaultValue Default value
|
||||||
|
bool readBoolean(const PDFObject& object, bool defaultValue) const;
|
||||||
|
|
||||||
/// Reads a text string from the object, if it is possible.
|
/// Reads a text string from the object, if it is possible.
|
||||||
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
||||||
/// \param defaultValue Default value
|
/// \param defaultValue Default value
|
||||||
@ -206,6 +211,12 @@ public:
|
|||||||
/// \param object Object containing array of numbers
|
/// \param object Object containing array of numbers
|
||||||
std::vector<PDFInteger> readIntegerArray(const PDFObject& object) const;
|
std::vector<PDFInteger> readIntegerArray(const PDFObject& object) const;
|
||||||
|
|
||||||
|
/// Reads boolean from dictionary. If dictionary entry doesn't exist, or error occurs, default value is returned.
|
||||||
|
/// \param dictionary Dictionary containing desired data
|
||||||
|
/// \param key Entry key
|
||||||
|
/// \param defaultValue Default value
|
||||||
|
bool readBooleanFromDictionary(const PDFDictionary* dictionary, const char* key, bool defaultValue) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const PDFDocument* m_document;
|
const PDFDocument* m_document;
|
||||||
};
|
};
|
||||||
|
28
PdfForQtLib/sources/pdffont.cpp
Normal file
28
PdfForQtLib/sources/pdffont.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (C) 2019 Jakub Melka
|
||||||
|
//
|
||||||
|
// This file is part of PdfForQt.
|
||||||
|
//
|
||||||
|
// PdfForQt is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// PdfForQt is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "pdffont.h"
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
PDFFont::PDFFont()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pdf
|
95
PdfForQtLib/sources/pdffont.h
Normal file
95
PdfForQtLib/sources/pdffont.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (C) 2019 Jakub Melka
|
||||||
|
//
|
||||||
|
// This file is part of PdfForQt.
|
||||||
|
//
|
||||||
|
// PdfForQt is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// PdfForQt is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef PDFFONT_H
|
||||||
|
#define PDFFONT_H
|
||||||
|
|
||||||
|
#include "pdfglobal.h"
|
||||||
|
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class TextRenderingMode
|
||||||
|
{
|
||||||
|
Fill = 0,
|
||||||
|
Stroke = 1,
|
||||||
|
FillStroke = 2,
|
||||||
|
Invisible = 3,
|
||||||
|
FillClip = 4,
|
||||||
|
StrokeClip = 5,
|
||||||
|
FillStrokeClip = 6,
|
||||||
|
Clip = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool isTextRenderingModeFilled(TextRenderingMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case TextRenderingMode::Fill:
|
||||||
|
case TextRenderingMode::FillClip:
|
||||||
|
case TextRenderingMode::FillStroke:
|
||||||
|
case TextRenderingMode::FillStrokeClip:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool isTextRenderingModeStroked(TextRenderingMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case TextRenderingMode::Stroke:
|
||||||
|
case TextRenderingMode::FillStroke:
|
||||||
|
case TextRenderingMode::StrokeClip:
|
||||||
|
case TextRenderingMode::FillStrokeClip:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool isTextRenderingModeClipped(TextRenderingMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case TextRenderingMode::Clip:
|
||||||
|
case TextRenderingMode::FillClip:
|
||||||
|
case TextRenderingMode::StrokeClip:
|
||||||
|
case TextRenderingMode::FillStrokeClip:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PDFFont
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PDFFont();
|
||||||
|
};
|
||||||
|
|
||||||
|
using PDFFontPointer = QSharedPointer<PDFFont>;
|
||||||
|
|
||||||
|
} // namespace pdf
|
||||||
|
|
||||||
|
#endif // PDFFONT_H
|
4543
PdfForQtLib/sources/pdfnametounicode.cpp
Normal file
4543
PdfForQtLib/sources/pdfnametounicode.cpp
Normal file
File diff suppressed because it is too large
Load Diff
61
PdfForQtLib/sources/pdfnametounicode.h
Normal file
61
PdfForQtLib/sources/pdfnametounicode.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (C) 2018 Jakub Melka
|
||||||
|
//
|
||||||
|
// This file is part of PdfForQt.
|
||||||
|
//
|
||||||
|
// PdfForQt is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// PdfForQt is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef PDFNAMETOUNICODE_H
|
||||||
|
#define PDFNAMETOUNICODE_H
|
||||||
|
|
||||||
|
#include "pdfglobal.h"
|
||||||
|
|
||||||
|
#include <QChar>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
class PDFFORQTLIBSHARED_EXPORT PDFNameToUnicode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFNameToUnicode() = delete;
|
||||||
|
|
||||||
|
/// Returns unicode character for name. If name is not found, then null character is returned.
|
||||||
|
static QChar getUnicodeForName(const QByteArray& name);
|
||||||
|
|
||||||
|
/// Returns unicode character for name (for ZapfDingbats). If name is not found, then null character is returned.
|
||||||
|
static QChar getUnicodeForNameZapfDingbats(const QByteArray& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Comparator
|
||||||
|
{
|
||||||
|
inline bool operator()(const QByteArray& left, const std::pair<QChar, const char*>& right)
|
||||||
|
{
|
||||||
|
return left < right.second;
|
||||||
|
}
|
||||||
|
inline bool operator()(const std::pair<QChar, const char*>& left, const QByteArray& right)
|
||||||
|
{
|
||||||
|
return left.second < right;
|
||||||
|
}
|
||||||
|
inline bool operator()(const std::pair<QChar, const char*>& left, const std::pair<QChar, const char*>& right)
|
||||||
|
{
|
||||||
|
return QLatin1String(left.second) < QLatin1String(right.second);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pdf
|
||||||
|
|
||||||
|
#endif // PDFNAMETOUNICODE_H
|
@ -152,7 +152,8 @@ static constexpr const std::pair<const char*, PDFPageContentProcessor::Operator>
|
|||||||
PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document) :
|
PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document) :
|
||||||
m_page(page),
|
m_page(page),
|
||||||
m_document(document),
|
m_document(document),
|
||||||
m_colorSpaceDictionary(nullptr)
|
m_colorSpaceDictionary(nullptr),
|
||||||
|
m_textBeginEndState(0)
|
||||||
{
|
{
|
||||||
Q_ASSERT(page);
|
Q_ASSERT(page);
|
||||||
Q_ASSERT(document);
|
Q_ASSERT(document);
|
||||||
@ -597,6 +598,97 @@ void PDFPageContentProcessor::processCommand(const QByteArray& command)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Operator::TextBegin:
|
||||||
|
{
|
||||||
|
// BT, begin text object, initialize text matrices, cannot be nested
|
||||||
|
operatorTextBegin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextEnd:
|
||||||
|
{
|
||||||
|
// ET, end text object, cannot be nested
|
||||||
|
operatorTextEnd();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextSetCharacterSpacing:
|
||||||
|
{
|
||||||
|
// Tc, set text character spacing
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextSetCharacterSpacing);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextSetWordSpacing:
|
||||||
|
{
|
||||||
|
// Tw, set text word spacing
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextSetWordSpacing);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextSetHorizontalScale:
|
||||||
|
{
|
||||||
|
// Tz, set text horizontal scaling (in percents, 100% = normal scaling)
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextSetHorizontalScale);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextSetLeading:
|
||||||
|
{
|
||||||
|
// TL, set text leading
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextSetLeading);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextSetFontAndFontSize:
|
||||||
|
{
|
||||||
|
// Tf, set text font (name from dictionary) and its size
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextSetFontAndFontSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextSetRenderMode:
|
||||||
|
{
|
||||||
|
// Tr, set text render mode
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextSetRenderMode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextSetRise:
|
||||||
|
{
|
||||||
|
// Ts, set text rise
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextSetRise);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextMoveByOffset:
|
||||||
|
{
|
||||||
|
// Td, move by offset
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextMoveByOffset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextSetLeadingAndMoveByOffset:
|
||||||
|
{
|
||||||
|
// TD, sets text leading and moves by offset, x y TD is equivalent to sequence -y TL x y Td
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextSetLeadingAndMoveByOffset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextSetMatrix:
|
||||||
|
{
|
||||||
|
// Tm, set text matrix
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorTextSetMatrix);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Operator::TextMoveByLeading:
|
||||||
|
{
|
||||||
|
// T*, moves text by leading, equivalent to 0 leading Td
|
||||||
|
operatorTextMoveByLeading();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Operator::Invalid:
|
case Operator::Invalid:
|
||||||
{
|
{
|
||||||
m_errorList.append(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Unknown operator '%1'.").arg(QString::fromLatin1(command))));
|
m_errorList.append(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Unknown operator '%1'.").arg(QString::fromLatin1(command))));
|
||||||
@ -867,6 +959,7 @@ void PDFPageContentProcessor::operatorSetGraphicState(PDFName dictionaryName)
|
|||||||
|
|
||||||
const PDFReal flatness = loader.readNumberFromDictionary(graphicStateDictionary, "FL", m_graphicState.getFlatness());
|
const PDFReal flatness = loader.readNumberFromDictionary(graphicStateDictionary, "FL", m_graphicState.getFlatness());
|
||||||
const PDFReal smoothness = loader.readNumberFromDictionary(graphicStateDictionary, "SM", m_graphicState.getSmoothness());
|
const PDFReal smoothness = loader.readNumberFromDictionary(graphicStateDictionary, "SM", m_graphicState.getSmoothness());
|
||||||
|
const bool textKnockout = loader.readBooleanFromDictionary(graphicStateDictionary, "TK", m_graphicState.getTextKnockout());
|
||||||
|
|
||||||
m_graphicState.setLineWidth(lineWidth);
|
m_graphicState.setLineWidth(lineWidth);
|
||||||
m_graphicState.setLineCapStyle(penCapStyle);
|
m_graphicState.setLineCapStyle(penCapStyle);
|
||||||
@ -874,6 +967,7 @@ void PDFPageContentProcessor::operatorSetGraphicState(PDFName dictionaryName)
|
|||||||
m_graphicState.setMitterLimit(mitterLimit);
|
m_graphicState.setMitterLimit(mitterLimit);
|
||||||
m_graphicState.setFlatness(flatness);
|
m_graphicState.setFlatness(flatness);
|
||||||
m_graphicState.setSmoothness(smoothness);
|
m_graphicState.setSmoothness(smoothness);
|
||||||
|
m_graphicState.setTextKnockout(textKnockout);
|
||||||
updateGraphicState();
|
updateGraphicState();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1315,6 +1409,130 @@ void PDFPageContentProcessor::operatorColorSetDeviceCMYKFilling(PDFReal c, PDFRe
|
|||||||
updateGraphicState();
|
updateGraphicState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextBegin()
|
||||||
|
{
|
||||||
|
m_graphicState.setTextMatrix(QMatrix());
|
||||||
|
m_graphicState.setTextLineMatrix(QMatrix());
|
||||||
|
updateGraphicState();
|
||||||
|
|
||||||
|
++m_textBeginEndState;
|
||||||
|
|
||||||
|
if (m_textBeginEndState > 1)
|
||||||
|
{
|
||||||
|
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Text object already started."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextEnd()
|
||||||
|
{
|
||||||
|
if (--m_textBeginEndState < 0)
|
||||||
|
{
|
||||||
|
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Text object ended more than once."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextSetCharacterSpacing(PDFReal charSpacing)
|
||||||
|
{
|
||||||
|
m_graphicState.setTextCharacterSpacing(charSpacing);
|
||||||
|
updateGraphicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextSetWordSpacing(PDFReal wordSpacing)
|
||||||
|
{
|
||||||
|
m_graphicState.setTextWordSpacing(wordSpacing);
|
||||||
|
updateGraphicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextSetHorizontalScale(PDFReal horizontalScaling)
|
||||||
|
{
|
||||||
|
// We disable horizontal scaling to less than 1%
|
||||||
|
horizontalScaling = qMax(horizontalScaling, 1.0);
|
||||||
|
|
||||||
|
m_graphicState.setTextHorizontalScaling(horizontalScaling / 100.0);
|
||||||
|
updateGraphicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextSetLeading(PDFReal leading)
|
||||||
|
{
|
||||||
|
m_graphicState.setTextLeading(leading);
|
||||||
|
updateGraphicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextSetFontAndFontSize(PDFPageContentProcessor::PDFName fontName, PDFReal fontSize)
|
||||||
|
{
|
||||||
|
Q_UNUSED(fontName);
|
||||||
|
Q_UNUSED(fontSize);
|
||||||
|
|
||||||
|
// TODO: Implement this operator
|
||||||
|
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Set font not implemented."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextSetRenderMode(PDFInteger mode)
|
||||||
|
{
|
||||||
|
mode = qBound<PDFInteger>(0, mode, 7);
|
||||||
|
m_graphicState.setTextRenderingMode(static_cast<TextRenderingMode>(mode));
|
||||||
|
updateGraphicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextSetRise(PDFReal rise)
|
||||||
|
{
|
||||||
|
m_graphicState.setTextRise(rise);
|
||||||
|
updateGraphicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextMoveByOffset(PDFReal t_x, PDFReal t_y)
|
||||||
|
{
|
||||||
|
const QMatrix& textLineMatrix = m_graphicState.getTextLineMatrix();
|
||||||
|
|
||||||
|
QMatrix translationMatrix;
|
||||||
|
translationMatrix.translate(t_x, t_y);
|
||||||
|
|
||||||
|
QMatrix resultMatrix = textLineMatrix * translationMatrix;
|
||||||
|
m_graphicState.setTextMatrix(resultMatrix);
|
||||||
|
m_graphicState.setTextLineMatrix(resultMatrix);
|
||||||
|
updateGraphicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextSetLeadingAndMoveByOffset(PDFReal t_x, PDFReal t_y)
|
||||||
|
{
|
||||||
|
// Update of graphic state is
|
||||||
|
m_graphicState.setTextLeading(-t_y);
|
||||||
|
operatorTextMoveByOffset(t_x, t_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextSetMatrix(PDFReal a, PDFReal b, PDFReal c, PDFReal d, PDFReal e, PDFReal f)
|
||||||
|
{
|
||||||
|
// We will comment following equation:
|
||||||
|
// Adobe PDF Reference 1.7 says, that we have this transformation using coefficient a, b, c, d, e and f:
|
||||||
|
// [ a, b, 0 ]
|
||||||
|
// [x', y', 1] = [ x, y, 1] * [ c, d, 0 ]
|
||||||
|
// [ e, f, 1 ]
|
||||||
|
// If we transpose this equation (we want this, because Qt uses transposed matrices (QMatrix).
|
||||||
|
// So, we will get following result:
|
||||||
|
//
|
||||||
|
// [ x' ] [ a, c, e] [ x ]
|
||||||
|
// [ y' ] = [ b, d, f] * [ y ]
|
||||||
|
// [ 1 ] [ 0, 0, 1] [ 1 ]
|
||||||
|
//
|
||||||
|
// So, it is obvious, than we will have following coefficients:
|
||||||
|
// m_11 = a, m_21 = c, dx = e
|
||||||
|
// m_12 = b, m_22 = d, dy = f
|
||||||
|
//
|
||||||
|
// We must also check, that matrix is invertible. If it is not, then we will throw exception
|
||||||
|
// to avoid errors later (for some operations, we assume matrix is invertible).
|
||||||
|
|
||||||
|
QMatrix matrix(a, b, c, d, e, f);
|
||||||
|
|
||||||
|
m_graphicState.setTextMatrix(matrix);
|
||||||
|
m_graphicState.setTextLineMatrix(matrix);
|
||||||
|
updateGraphicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorTextMoveByLeading()
|
||||||
|
{
|
||||||
|
operatorTextMoveByOffset(0.0, m_graphicState.getTextLeading());
|
||||||
|
}
|
||||||
|
|
||||||
PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() :
|
PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() :
|
||||||
m_currentTransformationMatrix(),
|
m_currentTransformationMatrix(),
|
||||||
m_fillColorSpace(),
|
m_fillColorSpace(),
|
||||||
@ -1328,6 +1546,14 @@ PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorSt
|
|||||||
m_renderingIntent(),
|
m_renderingIntent(),
|
||||||
m_flatness(1.0),
|
m_flatness(1.0),
|
||||||
m_smoothness(0.01),
|
m_smoothness(0.01),
|
||||||
|
m_textCharacterSpacing(0.0),
|
||||||
|
m_textWordSpacing(0.0),
|
||||||
|
m_textHorizontalScaling(100.0),
|
||||||
|
m_textLeading(0.0),
|
||||||
|
m_textFontSize(0.0),
|
||||||
|
m_textRenderingMode(TextRenderingMode::Fill),
|
||||||
|
m_textRise(0.0),
|
||||||
|
m_textKnockout(true),
|
||||||
m_stateFlags(StateUnchanged)
|
m_stateFlags(StateUnchanged)
|
||||||
{
|
{
|
||||||
m_fillColorSpace.reset(new PDFDeviceGrayColorSpace);
|
m_fillColorSpace.reset(new PDFDeviceGrayColorSpace);
|
||||||
@ -1354,6 +1580,17 @@ PDFPageContentProcessor::PDFPageContentProcessorState& PDFPageContentProcessor::
|
|||||||
setRenderingIntent(other.getRenderingIntent());
|
setRenderingIntent(other.getRenderingIntent());
|
||||||
setFlatness(other.getFlatness());
|
setFlatness(other.getFlatness());
|
||||||
setSmoothness(other.getSmoothness());
|
setSmoothness(other.getSmoothness());
|
||||||
|
setTextCharacterSpacing(other.getTextCharacterSpacing());
|
||||||
|
setTextWordSpacing(other.getTextWordSpacing());
|
||||||
|
setTextHorizontalScaling(other.getTextHorizontalScaling());
|
||||||
|
setTextLeading(other.getTextLeading());
|
||||||
|
setTextFont(other.getTextFont());
|
||||||
|
setTextFontSize(other.getTextFontSize());
|
||||||
|
setTextRenderingMode(other.getTextRenderingMode());
|
||||||
|
setTextRise(other.getTextRise());
|
||||||
|
setTextKnockout(other.getTextKnockout());
|
||||||
|
setTextMatrix(other.getTextMatrix());
|
||||||
|
setTextLineMatrix(other.getTextLineMatrix());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1429,7 +1666,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setLineJoinStyle(Qt:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFPageContentProcessor::PDFPageContentProcessorState::setMitterLimit(const PDFReal& mitterLimit)
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setMitterLimit(PDFReal mitterLimit)
|
||||||
{
|
{
|
||||||
if (m_mitterLimit != mitterLimit)
|
if (m_mitterLimit != mitterLimit)
|
||||||
{
|
{
|
||||||
@ -1474,4 +1711,103 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setSmoothness(PDFRea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextLeading(PDFReal textLeading)
|
||||||
|
{
|
||||||
|
if (m_textLeading != textLeading)
|
||||||
|
{
|
||||||
|
m_textLeading = textLeading;
|
||||||
|
m_stateFlags |= StateTextLeading;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextFontSize(PDFReal textFontSize)
|
||||||
|
{
|
||||||
|
if (m_textFontSize != textFontSize)
|
||||||
|
{
|
||||||
|
m_textFontSize = textFontSize;
|
||||||
|
m_stateFlags |= StateTextFontSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextKnockout(bool textKnockout)
|
||||||
|
{
|
||||||
|
if (m_textKnockout != textKnockout)
|
||||||
|
{
|
||||||
|
m_textKnockout = textKnockout;
|
||||||
|
m_stateFlags |= StateTextKnockout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextLineMatrix(const QMatrix& textLineMatrix)
|
||||||
|
{
|
||||||
|
if (m_textLineMatrix != textLineMatrix)
|
||||||
|
{
|
||||||
|
m_textLineMatrix = textLineMatrix;
|
||||||
|
m_stateFlags |= StateTextLineMatrix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextMatrix(const QMatrix& textMatrix)
|
||||||
|
{
|
||||||
|
if (m_textMatrix != textMatrix)
|
||||||
|
{
|
||||||
|
m_textMatrix = textMatrix;
|
||||||
|
m_stateFlags |= StateTextMatrix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextRise(PDFReal textRise)
|
||||||
|
{
|
||||||
|
if (m_textRise != textRise)
|
||||||
|
{
|
||||||
|
m_textRise = textRise;
|
||||||
|
m_stateFlags |= StateTextRise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextRenderingMode(TextRenderingMode textRenderingMode)
|
||||||
|
{
|
||||||
|
if (m_textRenderingMode != textRenderingMode)
|
||||||
|
{
|
||||||
|
m_textRenderingMode = textRenderingMode;
|
||||||
|
m_stateFlags |= StateTextRenderingMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextFont(const PDFFontPointer& textFont)
|
||||||
|
{
|
||||||
|
if (m_textFont != textFont)
|
||||||
|
{
|
||||||
|
m_textFont = textFont;
|
||||||
|
m_stateFlags |= StateTextFont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextHorizontalScaling(PDFReal textHorizontalScaling)
|
||||||
|
{
|
||||||
|
if (m_textHorizontalScaling != textHorizontalScaling)
|
||||||
|
{
|
||||||
|
m_textHorizontalScaling = textHorizontalScaling;
|
||||||
|
m_stateFlags |= StateTextHorizontalScaling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextWordSpacing(PDFReal textWordSpacing)
|
||||||
|
{
|
||||||
|
if (m_textWordSpacing != textWordSpacing)
|
||||||
|
{
|
||||||
|
m_textWordSpacing = textWordSpacing;
|
||||||
|
m_stateFlags |= StateTextWordSpacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::PDFPageContentProcessorState::setTextCharacterSpacing(PDFReal textCharacterSpacing)
|
||||||
|
{
|
||||||
|
if (m_textCharacterSpacing != textCharacterSpacing)
|
||||||
|
{
|
||||||
|
m_textCharacterSpacing = textCharacterSpacing;
|
||||||
|
m_stateFlags |= StateTextCharacterSpacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "pdfrenderer.h"
|
#include "pdfrenderer.h"
|
||||||
#include "pdfparser.h"
|
#include "pdfparser.h"
|
||||||
#include "pdfcolorspaces.h"
|
#include "pdfcolorspaces.h"
|
||||||
|
#include "pdffont.h"
|
||||||
|
|
||||||
#include <QMatrix>
|
#include <QMatrix>
|
||||||
#include <QPainterPath>
|
#include <QPainterPath>
|
||||||
@ -113,7 +114,7 @@ public:
|
|||||||
|
|
||||||
// Text positioning: Td, TD, Tm, T*
|
// Text positioning: Td, TD, Tm, T*
|
||||||
TextMoveByOffset, ///< Td, move by offset
|
TextMoveByOffset, ///< Td, move by offset
|
||||||
TextSetLeadingAndMoveByOffset, ///< TD, sets thext leading and moves by offset, x y TD is equivalent to sequence -y TL x y Td
|
TextSetLeadingAndMoveByOffset, ///< TD, sets text leading and moves by offset, x y TD is equivalent to sequence -y TL x y Td
|
||||||
TextSetMatrix, ///< Tm, set text matrix
|
TextSetMatrix, ///< Tm, set text matrix
|
||||||
TextMoveByLeading, ///< T*, moves text by leading, equivalent to 0 leading Td
|
TextMoveByLeading, ///< T*, moves text by leading, equivalent to 0 leading Td
|
||||||
|
|
||||||
@ -214,20 +215,31 @@ protected:
|
|||||||
|
|
||||||
enum StateFlag
|
enum StateFlag
|
||||||
{
|
{
|
||||||
StateUnchanged = 0x0000,
|
StateUnchanged = 0x00000000,
|
||||||
StateCurrentTransformationMatrix = 0x0001,
|
StateCurrentTransformationMatrix = 0x00000001,
|
||||||
StateStrokeColorSpace = 0x0002,
|
StateStrokeColorSpace = 0x00000002,
|
||||||
StateFillColorSpace = 0x0004,
|
StateFillColorSpace = 0x00000004,
|
||||||
StateStrokeColor = 0x0008,
|
StateStrokeColor = 0x00000008,
|
||||||
StateFillColor = 0x0010,
|
StateFillColor = 0x00000010,
|
||||||
StateLineWidth = 0x0020,
|
StateLineWidth = 0x00000020,
|
||||||
StateLineCapStyle = 0x0040,
|
StateLineCapStyle = 0x00000040,
|
||||||
StateLineJoinStyle = 0x0080,
|
StateLineJoinStyle = 0x00000080,
|
||||||
StateMitterLimit = 0x0100,
|
StateMitterLimit = 0x00000100,
|
||||||
StateLineDashPattern = 0x0200,
|
StateLineDashPattern = 0x00000200,
|
||||||
StateRenderingIntent = 0x0400,
|
StateRenderingIntent = 0x00000400,
|
||||||
StateFlatness = 0x0800,
|
StateFlatness = 0x00000800,
|
||||||
StateSmoothness = 0x1000,
|
StateSmoothness = 0x00001000,
|
||||||
|
StateTextMatrix = 0x00002000,
|
||||||
|
StateTextLineMatrix = 0x00004000,
|
||||||
|
StateTextCharacterSpacing = 0x00008000,
|
||||||
|
StateTextWordSpacing = 0x00010000,
|
||||||
|
StateTextHorizontalScaling = 0x00020000,
|
||||||
|
StateTextLeading = 0x00040000,
|
||||||
|
StateTextFont = 0x00080000,
|
||||||
|
StateTextFontSize = 0x00100000,
|
||||||
|
StateTextRenderingMode = 0x00200000,
|
||||||
|
StateTextRise = 0x00400000,
|
||||||
|
StateTextKnockout = 0x00800000,
|
||||||
StateAll = 0xFFFF
|
StateAll = 0xFFFF
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -258,7 +270,7 @@ protected:
|
|||||||
void setLineJoinStyle(Qt::PenJoinStyle lineJoinStyle);
|
void setLineJoinStyle(Qt::PenJoinStyle lineJoinStyle);
|
||||||
|
|
||||||
PDFReal getMitterLimit() const { return m_mitterLimit; }
|
PDFReal getMitterLimit() const { return m_mitterLimit; }
|
||||||
void setMitterLimit(const PDFReal& mitterLimit);
|
void setMitterLimit(PDFReal mitterLimit);
|
||||||
|
|
||||||
const PDFLineDashPattern& getLineDashPattern() const { return m_lineDashPattern; }
|
const PDFLineDashPattern& getLineDashPattern() const { return m_lineDashPattern; }
|
||||||
void setLineDashPattern(PDFLineDashPattern pattern);
|
void setLineDashPattern(PDFLineDashPattern pattern);
|
||||||
@ -275,6 +287,39 @@ protected:
|
|||||||
StateFlags getStateFlags() const { return m_stateFlags; }
|
StateFlags getStateFlags() const { return m_stateFlags; }
|
||||||
void setStateFlags(StateFlags stateFlags) { m_stateFlags = stateFlags; }
|
void setStateFlags(StateFlags stateFlags) { m_stateFlags = stateFlags; }
|
||||||
|
|
||||||
|
PDFReal getTextCharacterSpacing() const { return m_textCharacterSpacing; }
|
||||||
|
void setTextCharacterSpacing(PDFReal textCharacterSpacing);
|
||||||
|
|
||||||
|
PDFReal getTextWordSpacing() const { return m_textWordSpacing; }
|
||||||
|
void setTextWordSpacing(PDFReal textWordSpacing);
|
||||||
|
|
||||||
|
PDFReal getTextHorizontalScaling() const { return m_textHorizontalScaling; }
|
||||||
|
void setTextHorizontalScaling(PDFReal textHorizontalScaling);
|
||||||
|
|
||||||
|
PDFReal getTextLeading() const { return m_textLeading; }
|
||||||
|
void setTextLeading(PDFReal textLeading);
|
||||||
|
|
||||||
|
const PDFFontPointer& getTextFont() const { return m_textFont; }
|
||||||
|
void setTextFont(const PDFFontPointer& textFont);
|
||||||
|
|
||||||
|
PDFReal getTextFontSize() const { return m_textFontSize; }
|
||||||
|
void setTextFontSize(PDFReal textFontSize);
|
||||||
|
|
||||||
|
TextRenderingMode getTextRenderingMode() const { return m_textRenderingMode; }
|
||||||
|
void setTextRenderingMode(TextRenderingMode textRenderingMode);
|
||||||
|
|
||||||
|
PDFReal getTextRise() const { return m_textRise; }
|
||||||
|
void setTextRise(PDFReal textRise);
|
||||||
|
|
||||||
|
bool getTextKnockout() const { return m_textKnockout; }
|
||||||
|
void setTextKnockout(bool textKnockout);
|
||||||
|
|
||||||
|
const QMatrix& getTextMatrix() const { return m_textMatrix; }
|
||||||
|
void setTextMatrix(const QMatrix& textMatrix);
|
||||||
|
|
||||||
|
const QMatrix& getTextLineMatrix() const { return m_textLineMatrix; }
|
||||||
|
void setTextLineMatrix(const QMatrix& textLineMatrix);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMatrix m_currentTransformationMatrix;
|
QMatrix m_currentTransformationMatrix;
|
||||||
PDFColorSpacePointer m_strokeColorSpace;
|
PDFColorSpacePointer m_strokeColorSpace;
|
||||||
@ -289,6 +334,17 @@ protected:
|
|||||||
QByteArray m_renderingIntent;
|
QByteArray m_renderingIntent;
|
||||||
PDFReal m_flatness;
|
PDFReal m_flatness;
|
||||||
PDFReal m_smoothness;
|
PDFReal m_smoothness;
|
||||||
|
PDFReal m_textCharacterSpacing; // T_c
|
||||||
|
PDFReal m_textWordSpacing; // T_w
|
||||||
|
PDFReal m_textHorizontalScaling; // T_h, percentage
|
||||||
|
PDFReal m_textLeading; // T_l
|
||||||
|
PDFFontPointer m_textFont; // Text font
|
||||||
|
PDFReal m_textFontSize; // T_fs
|
||||||
|
TextRenderingMode m_textRenderingMode; // Text rendering mode
|
||||||
|
PDFReal m_textRise; // T_rise
|
||||||
|
bool m_textKnockout;
|
||||||
|
QMatrix m_textMatrix;
|
||||||
|
QMatrix m_textLineMatrix;
|
||||||
StateFlags m_stateFlags;
|
StateFlags m_stateFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -475,6 +531,25 @@ private:
|
|||||||
void operatorColorSetDeviceCMYKStroking(PDFReal c, PDFReal m, PDFReal y, PDFReal k); ///< K, set DeviceCMYK color space for stroking color and set color
|
void operatorColorSetDeviceCMYKStroking(PDFReal c, PDFReal m, PDFReal y, PDFReal k); ///< K, set DeviceCMYK color space for stroking color and set color
|
||||||
void operatorColorSetDeviceCMYKFilling(PDFReal c, PDFReal m, PDFReal y, PDFReal k); ///< k, set DeviceCMYK color space for filling color and set color
|
void operatorColorSetDeviceCMYKFilling(PDFReal c, PDFReal m, PDFReal y, PDFReal k); ///< k, set DeviceCMYK color space for filling color and set color
|
||||||
|
|
||||||
|
// Text object: BT, ET
|
||||||
|
void operatorTextBegin(); ///< BT, begin text object, initialize text matrices, cannot be nested
|
||||||
|
void operatorTextEnd(); ///< ET, end text object, cannot be nested
|
||||||
|
|
||||||
|
// Text state: Tc, Tw, Tz, TL, Tf, Tr, Ts
|
||||||
|
void operatorTextSetCharacterSpacing(PDFReal charSpacing); ///< Tc, set text character spacing
|
||||||
|
void operatorTextSetWordSpacing(PDFReal wordSpacing); ///< Tw, set text word spacing
|
||||||
|
void operatorTextSetHorizontalScale(PDFReal horizontalScaling); ///< Tz, set text horizontal scaling (in percents, 100% = normal scaling)
|
||||||
|
void operatorTextSetLeading(PDFReal leading); ///< TL, set text leading
|
||||||
|
void operatorTextSetFontAndFontSize(PDFName fontName, PDFReal fontSize); ///< Tf, set text font (name from dictionary) and its size
|
||||||
|
void operatorTextSetRenderMode(PDFInteger mode); ///< Tr, set text render mode
|
||||||
|
void operatorTextSetRise(PDFReal rise); ///< Ts, set text rise
|
||||||
|
|
||||||
|
// Text positioning: Td, TD, Tm, T*
|
||||||
|
void operatorTextMoveByOffset(PDFReal t_x, PDFReal t_y); ///< Td, move by offset
|
||||||
|
void operatorTextSetLeadingAndMoveByOffset(PDFReal t_x, PDFReal t_y); ///< TD, sets text leading and moves by offset, x y TD is equivalent to sequence -y TL x y Td
|
||||||
|
void operatorTextSetMatrix(PDFReal a, PDFReal b, PDFReal c, PDFReal d, PDFReal e, PDFReal f); ///< Tm, set text matrix
|
||||||
|
void operatorTextMoveByLeading(); ///< T*, moves text by leading, equivalent to 0 leading Td
|
||||||
|
|
||||||
const PDFPage* m_page;
|
const PDFPage* m_page;
|
||||||
const PDFDocument* m_document;
|
const PDFDocument* m_document;
|
||||||
const PDFDictionary* m_colorSpaceDictionary;
|
const PDFDictionary* m_colorSpaceDictionary;
|
||||||
@ -498,6 +573,9 @@ private:
|
|||||||
|
|
||||||
/// Current painter path
|
/// Current painter path
|
||||||
QPainterPath m_currentPath;
|
QPainterPath m_currentPath;
|
||||||
|
|
||||||
|
/// Nesting level of the begin/end of text object
|
||||||
|
int m_textBeginEndState;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
Reference in New Issue
Block a user