mirror of https://github.com/JakubMelka/PDF4QT.git
Fix of font drawing
This commit is contained in:
parent
a462e0b768
commit
cd981183b0
|
@ -273,6 +273,17 @@ QByteArray PDFDocumentDataLoaderDecorator::readName(const PDFObject& object)
|
|||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray PDFDocumentDataLoaderDecorator::readString(const PDFObject& object)
|
||||
{
|
||||
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||
if (dereferencedObject.isString())
|
||||
{
|
||||
return dereferencedObject.getString();
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
PDFInteger PDFDocumentDataLoaderDecorator::readInteger(const PDFObject& object, PDFInteger defaultValue) const
|
||||
{
|
||||
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||
|
@ -479,4 +490,14 @@ QByteArray PDFDocumentDataLoaderDecorator::readNameFromDictionary(const PDFDicti
|
|||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray PDFDocumentDataLoaderDecorator::readStringFromDictionary(const PDFDictionary* dictionary, const char* key)
|
||||
{
|
||||
if (dictionary->hasKey(key))
|
||||
{
|
||||
return readString(dictionary->get(key));
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
|
|
@ -92,6 +92,11 @@ public:
|
|||
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
||||
QByteArray readName(const PDFObject& object);
|
||||
|
||||
/// Reads a string from the object, if it is possible. If object is not a string,
|
||||
/// then empty byte array is returned.
|
||||
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
||||
QByteArray readString(const PDFObject& object);
|
||||
|
||||
/// Reads an integer from the object, if it is possible.
|
||||
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
||||
/// \param defaultValue Default value
|
||||
|
@ -227,6 +232,11 @@ public:
|
|||
/// \param key Entry key
|
||||
QByteArray readNameFromDictionary(const PDFDictionary* dictionary, const char* key);
|
||||
|
||||
/// Reads a string from dictionary. If dictionary entry doesn't exist, or error occurs, empty byte array is returned.
|
||||
/// \param dictionary Dictionary containing desired data
|
||||
/// \param key Entry key
|
||||
QByteArray readStringFromDictionary(const PDFDictionary* dictionary, const char* key);
|
||||
|
||||
private:
|
||||
const PDFDocument* m_document;
|
||||
};
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
namespace pdf
|
||||
{
|
||||
|
||||
PDFFont::PDFFont()
|
||||
PDFFont::PDFFont(FontDescriptor fontDescriptor) :
|
||||
m_fontDescriptor(qMove(fontDescriptor))
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -78,7 +79,57 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d
|
|||
const StandardFontType standardFont = fontLoader.readEnumByName(fontDictionary->get("BaseFont"), standardFonts.cbegin(), standardFonts.cend(), StandardFontType::Invalid);
|
||||
|
||||
// Read Font Descriptor
|
||||
// TODO: Read font descriptor
|
||||
FontDescriptor fontDescriptor;
|
||||
const PDFObject& fontDescriptorObject = document->getObject(fontDictionary->get("FontDescriptor"));
|
||||
if (fontDescriptorObject.isDictionary())
|
||||
{
|
||||
const PDFDictionary* fontDescriptorDictionary = fontDescriptorObject.getDictionary();
|
||||
fontDescriptor.fontName = fontLoader.readNameFromDictionary(fontDescriptorDictionary, "FontName");
|
||||
fontDescriptor.fontFamily = fontLoader.readStringFromDictionary(fontDescriptorDictionary, "FontFamily");
|
||||
|
||||
constexpr const std::array<std::pair<const char*, QFont::Stretch>, 9> stretches = {
|
||||
std::pair<const char*, QFont::Stretch>{ "UltraCondensed", QFont::UltraCondensed },
|
||||
std::pair<const char*, QFont::Stretch>{ "ExtraCondensed", QFont::ExtraCondensed },
|
||||
std::pair<const char*, QFont::Stretch>{ "Condensed", QFont::Condensed },
|
||||
std::pair<const char*, QFont::Stretch>{ "SemiCondensed", QFont::SemiCondensed },
|
||||
std::pair<const char*, QFont::Stretch>{ "Normal", QFont::Unstretched },
|
||||
std::pair<const char*, QFont::Stretch>{ "SemiExpanded", QFont::SemiExpanded },
|
||||
std::pair<const char*, QFont::Stretch>{ "Expanded", QFont::Expanded },
|
||||
std::pair<const char*, QFont::Stretch>{ "ExtraExpanded", QFont::ExtraExpanded },
|
||||
std::pair<const char*, QFont::Stretch>{ "UltraExpanded", QFont::UltraExpanded }
|
||||
};
|
||||
fontDescriptor.fontStretch = fontLoader.readEnumByName(fontDescriptorDictionary->get("FontStretch"), stretches.cbegin(), stretches.cend(), QFont::Unstretched);
|
||||
fontDescriptor.fontWeight = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "FontWeight", 500);
|
||||
fontDescriptor.italicAngle = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "ItalicAngle", 0.0);
|
||||
fontDescriptor.ascent = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "Ascent", 0.0);
|
||||
fontDescriptor.descent = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "Descent", 0.0);
|
||||
fontDescriptor.leading = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "Leading", 0.0);
|
||||
fontDescriptor.capHeight = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "CapHeight", 0.0);
|
||||
fontDescriptor.xHeight = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "XHeight", 0.0);
|
||||
fontDescriptor.stemV = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "StemV", 0.0);
|
||||
fontDescriptor.stemH = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "StemH", 0.0);
|
||||
fontDescriptor.avgWidth = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "AvgWidth", 0.0);
|
||||
fontDescriptor.maxWidth = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "MaxWidth", 0.0);
|
||||
fontDescriptor.missingWidth = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "MissingWidth", 0.0);
|
||||
fontDescriptor.flags = fontLoader.readIntegerFromDictionary(fontDescriptorDictionary, "Flags", 0);
|
||||
fontDescriptor.boundingBox = fontLoader.readRectangle(fontDescriptorDictionary->get("FontBBox"), QRectF());
|
||||
fontDescriptor.charset = fontLoader.readStringFromDictionary(fontDescriptorDictionary, "Charset");
|
||||
|
||||
auto loadStream = [fontDescriptorDictionary, document](QByteArray& byteArray, const char* name)
|
||||
{
|
||||
if (fontDescriptorDictionary->hasKey(name))
|
||||
{
|
||||
const PDFObject& streamObject = document->getObject(fontDescriptorDictionary->get(name));
|
||||
if (streamObject.isStream())
|
||||
{
|
||||
byteArray = document->getDecodedStream(streamObject.getStream());
|
||||
}
|
||||
}
|
||||
};
|
||||
loadStream(fontDescriptor.fontFile, "FontFile");
|
||||
loadStream(fontDescriptor.fontFile2, "FontFile2");
|
||||
loadStream(fontDescriptor.fontFile3, "FontFile3");
|
||||
}
|
||||
|
||||
// Read Font Encoding
|
||||
// The font encoding for the simple font is determined by this algorithm:
|
||||
|
@ -235,10 +286,10 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d
|
|||
switch (fontType)
|
||||
{
|
||||
case FontType::Type1:
|
||||
return PDFFontPointer(new PDFType1Font(qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, standardFont));
|
||||
return PDFFontPointer(new PDFType1Font(qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, standardFont));
|
||||
|
||||
case FontType::TrueType:
|
||||
return PDFFontPointer(new PDFTrueTypeFont(qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable));
|
||||
return PDFFontPointer(new PDFTrueTypeFont(qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable));
|
||||
|
||||
default:
|
||||
{
|
||||
|
@ -255,13 +306,15 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d
|
|||
return PDFFontPointer();
|
||||
}
|
||||
|
||||
PDFSimpleFont::PDFSimpleFont(QByteArray name,
|
||||
PDFSimpleFont::PDFSimpleFont(FontDescriptor fontDescriptor,
|
||||
QByteArray name,
|
||||
QByteArray baseFont,
|
||||
PDFInteger firstChar,
|
||||
PDFInteger lastChar,
|
||||
std::vector<PDFInteger> widths,
|
||||
PDFEncoding::Encoding encodingType,
|
||||
encoding::EncodingTable encoding) :
|
||||
PDFFont(qMove(fontDescriptor)),
|
||||
m_name(qMove(name)),
|
||||
m_baseFont(qMove(baseFont)),
|
||||
m_firstChar(firstChar),
|
||||
|
@ -276,10 +329,40 @@ PDFSimpleFont::PDFSimpleFont(QByteArray name,
|
|||
QRawFont PDFSimpleFont::getRealizedFont(PDFReal fontSize) const
|
||||
{
|
||||
// TODO: Fix font creation to use also embedded fonts, font descriptor, etc.
|
||||
QFont font(m_baseFont);
|
||||
font.setHintingPreference(QFont::PreferNoHinting);
|
||||
font.setPixelSize(fontSize);
|
||||
return QRawFont::fromFont(font, QFontDatabase::Any);
|
||||
QRawFont rawFont;
|
||||
|
||||
if (m_fontDescriptor.isEmbedded())
|
||||
{
|
||||
// Type 1 font
|
||||
if (!m_fontDescriptor.fontFile.isEmpty())
|
||||
{
|
||||
rawFont.loadFromData(m_fontDescriptor.fontFile, fontSize, QFont::PreferNoHinting);
|
||||
}
|
||||
else if (!m_fontDescriptor.fontFile2.isEmpty())
|
||||
{
|
||||
rawFont.loadFromData(m_fontDescriptor.fontFile2, fontSize, QFont::PreferNoHinting);
|
||||
}
|
||||
|
||||
if (!rawFont.isValid())
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Can't load embedded font."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Zkontrolovat, zda se zde opravdu prebiraji spravne fonty
|
||||
const int weight = qBound<int>(0, m_fontDescriptor.fontWeight / 10.0, 99);
|
||||
const int stretch = qBound<int>(1, m_fontDescriptor.fontStretch, 4000);
|
||||
|
||||
QFont font(m_baseFont);
|
||||
font.setHintingPreference(QFont::PreferNoHinting);
|
||||
font.setStretch(stretch);
|
||||
font.setWeight(weight);
|
||||
font.setPixelSize(fontSize);
|
||||
rawFont = QRawFont::fromFont(font, QFontDatabase::Any);
|
||||
}
|
||||
|
||||
return rawFont;
|
||||
}
|
||||
|
||||
QString PDFSimpleFont::getTextUsingEncoding(const QByteArray& byteArray) const
|
||||
|
@ -295,7 +378,8 @@ QString PDFSimpleFont::getTextUsingEncoding(const QByteArray& byteArray) const
|
|||
return string;
|
||||
}
|
||||
|
||||
PDFType1Font::PDFType1Font(QByteArray name,
|
||||
PDFType1Font::PDFType1Font(FontDescriptor fontDescriptor,
|
||||
QByteArray name,
|
||||
QByteArray baseFont,
|
||||
PDFInteger firstChar,
|
||||
PDFInteger lastChar,
|
||||
|
@ -303,7 +387,7 @@ PDFType1Font::PDFType1Font(QByteArray name,
|
|||
PDFEncoding::Encoding encodingType,
|
||||
encoding::EncodingTable encoding,
|
||||
StandardFontType standardFontType) :
|
||||
PDFSimpleFont(qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encodingType, encoding),
|
||||
PDFSimpleFont(qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encodingType, encoding),
|
||||
m_standardFontType(standardFontType)
|
||||
{
|
||||
|
||||
|
|
|
@ -129,6 +129,42 @@ static constexpr PDFEncoding::Encoding getEncodingForStandardFont(StandardFontTy
|
|||
}
|
||||
}
|
||||
|
||||
struct FontDescriptor
|
||||
{
|
||||
bool isEmbedded() const { return !fontFile.isEmpty() || !fontFile2.isEmpty() || !fontFile3.isEmpty(); }
|
||||
|
||||
QByteArray fontName;
|
||||
QByteArray fontFamily;
|
||||
QFont::Stretch fontStretch = QFont::AnyStretch;
|
||||
PDFReal fontWeight = 400.0;
|
||||
PDFInteger flags;
|
||||
QRectF boundingBox;
|
||||
PDFReal italicAngle = 0.0;
|
||||
PDFReal ascent = 0.0;
|
||||
PDFReal descent = 0.0;
|
||||
PDFReal leading = 0.0;
|
||||
PDFReal capHeight = 0.0;
|
||||
PDFReal xHeight = 0.0;
|
||||
PDFReal stemV = 0.0;
|
||||
PDFReal stemH = 0.0;
|
||||
PDFReal avgWidth = 0.0;
|
||||
PDFReal maxWidth = 0.0;
|
||||
PDFReal missingWidth = 0.0;
|
||||
|
||||
/// Byte array with Type 1 font program (embedded font)
|
||||
QByteArray fontFile;
|
||||
|
||||
/// Byte array with TrueType font program (embedded font)
|
||||
QByteArray fontFile2;
|
||||
|
||||
/// Byte array with font program, whose format is defined by the Subtype array
|
||||
/// in the font dictionary.
|
||||
QByteArray fontFile3;
|
||||
|
||||
/// Character set
|
||||
QByteArray charset;
|
||||
};
|
||||
|
||||
class PDFFont;
|
||||
|
||||
using PDFFontPointer = QSharedPointer<PDFFont>;
|
||||
|
@ -137,7 +173,7 @@ using PDFFontPointer = QSharedPointer<PDFFont>;
|
|||
class PDFFont
|
||||
{
|
||||
public:
|
||||
explicit PDFFont();
|
||||
explicit PDFFont(FontDescriptor fontDescriptor);
|
||||
virtual ~PDFFont() = default;
|
||||
|
||||
/// Returns the font type
|
||||
|
@ -152,7 +188,16 @@ public:
|
|||
/// \param byteArray Byte array with encoded string
|
||||
virtual QString getTextUsingEncoding(const QByteArray& byteArray) const = 0;
|
||||
|
||||
/// Returns font descriptor
|
||||
const FontDescriptor* getFontDescriptor() const { return &m_fontDescriptor; }
|
||||
|
||||
/// Creates font from the object. If font can't be created, exception is thrown.
|
||||
/// \param object Font dictionary
|
||||
/// \param document Document
|
||||
static PDFFontPointer createFont(const PDFObject& object, const PDFDocument* document);
|
||||
|
||||
protected:
|
||||
FontDescriptor m_fontDescriptor;
|
||||
};
|
||||
|
||||
/// Simple font, see PDF reference 1.7, chapter 5.5. Simple fonts have encoding table,
|
||||
|
@ -160,7 +205,8 @@ public:
|
|||
class PDFSimpleFont : public PDFFont
|
||||
{
|
||||
public:
|
||||
explicit PDFSimpleFont(QByteArray name,
|
||||
explicit PDFSimpleFont(FontDescriptor fontDescriptor,
|
||||
QByteArray name,
|
||||
QByteArray baseFont,
|
||||
PDFInteger firstChar,
|
||||
PDFInteger lastChar,
|
||||
|
@ -185,7 +231,8 @@ protected:
|
|||
class PDFType1Font : public PDFSimpleFont
|
||||
{
|
||||
public:
|
||||
explicit PDFType1Font(QByteArray name,
|
||||
explicit PDFType1Font(FontDescriptor fontDescriptor,
|
||||
QByteArray name,
|
||||
QByteArray baseFont,
|
||||
PDFInteger firstChar,
|
||||
PDFInteger lastChar,
|
||||
|
|
|
@ -1564,12 +1564,11 @@ void PDFPageContentProcessor::operatorTextMoveByOffset(PDFReal t_x, PDFReal t_y)
|
|||
{
|
||||
const QMatrix& textLineMatrix = m_graphicState.getTextLineMatrix();
|
||||
|
||||
QMatrix translationMatrix;
|
||||
translationMatrix.translate(t_x, t_y);
|
||||
QMatrix transformedMatrix = textLineMatrix;
|
||||
transformedMatrix.translate(t_x, t_y);
|
||||
|
||||
QMatrix resultMatrix = textLineMatrix * translationMatrix;
|
||||
m_graphicState.setTextMatrix(resultMatrix);
|
||||
m_graphicState.setTextLineMatrix(resultMatrix);
|
||||
m_graphicState.setTextMatrix(transformedMatrix);
|
||||
m_graphicState.setTextLineMatrix(transformedMatrix);
|
||||
updateGraphicState();
|
||||
}
|
||||
|
||||
|
@ -1705,6 +1704,7 @@ void PDFPageContentProcessor::operatorTextSetSpacingAndShowText(PDFReal t_w, PDF
|
|||
|
||||
void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
|
||||
{
|
||||
// TODO: Kdyz nejsme v text rezimu, tak nekreslime text
|
||||
if (textSequence.items.empty())
|
||||
{
|
||||
// Do not display empty text
|
||||
|
@ -1748,6 +1748,7 @@ void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
|
|||
const bool stroke = isTextRenderingModeStroked(textRenderingMode);
|
||||
const bool clipped = isTextRenderingModeClipped(textRenderingMode);
|
||||
// TODO: Add Text Clipping
|
||||
// TODO: Pouzit pravdepodobne sirky z widths array?
|
||||
|
||||
// Detect horizontal writing system
|
||||
const bool isHorizontalWritingSystem = std::any_of(advances.cbegin(), advances.cend(), [](const QPointF& point) { return !qFuzzyIsNull(point.x()); });
|
||||
|
|
Loading…
Reference in New Issue