// 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 . #ifndef PDFFONT_H #define PDFFONT_H #include "pdfglobal.h" #include "pdfencoding.h" #include "pdfobject.h" #include #include namespace pdf { class PDFDocument; enum class TextRenderingMode { Fill = 0, Stroke = 1, FillStroke = 2, Invisible = 3, FillClip = 4, StrokeClip = 5, FillStrokeClip = 6, Clip = 7 }; /// Item of the text sequence (either single character, or advance) struct TextSequenceItem { inline explicit TextSequenceItem() = default; inline explicit TextSequenceItem(const QPainterPath* glyph, QChar character, PDFReal advance) : glyph(glyph), character(character), advance(advance) { } inline explicit TextSequenceItem(PDFReal advance) : character(), advance(advance) { } inline bool isCharacter() const { return !character.isNull(); } inline bool isAdvance() const { return advance != 0.0; } inline bool isNull() const { return !isCharacter() && !isAdvance(); } const QPainterPath* glyph = nullptr; QChar character; PDFReal advance = 0; }; struct TextSequence { std::vector items; }; 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; } } enum class FontType { Invalid, Type1, TrueType }; /// Standard Type1 fonts enum class StandardFontType { Invalid, TimesRoman, TimesRomanBold, TimesRomanItalics, TimesRomanBoldItalics, Helvetica, HelveticaBold, HelveticaOblique, HelveticaBoldOblique, Courier, CourierBold, CourierOblique, CourierBoldOblique, Symbol, ZapfDingbats }; /// Returns builtin encoding for the standard font static constexpr PDFEncoding::Encoding getEncodingForStandardFont(StandardFontType standardFont) { switch (standardFont) { case StandardFontType::Symbol: return PDFEncoding::Encoding::Symbol; case StandardFontType::ZapfDingbats: return PDFEncoding::Encoding::ZapfDingbats; default: return PDFEncoding::Encoding::Standard; } } 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; class PDFRealizedFont; class PDFRealizedFontImpl; using PDFRealizedFontPointer = QSharedPointer; /// Font, which has fixed pixel size. It is programmed as PIMPL, because we need /// to remove FreeType types from the interface (so we do not include FreeType in the interface). class PDFRealizedFont { public: ~PDFRealizedFont(); /// Fills the text sequence by interpreting byte array according font data and /// produces glyphs for the font. /// \param byteArray Array of bytes to be interpreted /// \param textSequence Text sequence to be filled void fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence); /// Return true, if we have horizontal writing system bool isHorizontalWritingSystem() const; /// Creates new realized font from the standard font. If font can't be created, /// then exception is thrown. static PDFRealizedFontPointer createRealizedFont(PDFFontPointer font, PDFReal pixelSize); private: /// Constructs new realized font explicit PDFRealizedFont(PDFRealizedFontImpl* impl) : m_impl(impl) { } PDFRealizedFontImpl* m_impl; }; /// Base class representing font in the PDF file class PDFFont { public: explicit PDFFont(FontDescriptor fontDescriptor); virtual ~PDFFont() = default; /// Returns the font type virtual FontType getFontType() const = 0; /// Realizes the font (physical materialization of the font using pixel size, /// if font can't be realized, then empty QRawFont is returned). /// \param fontSize Size of the font virtual PDFRealizedFontPointer getRealizedFont(PDFFontPointer font, PDFReal fontSize) const = 0; /// Returns text using the font encoding /// \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, /// which maps single-byte character to the glyph in the font. class PDFSimpleFont : public PDFFont { public: explicit PDFSimpleFont(FontDescriptor fontDescriptor, QByteArray name, QByteArray baseFont, PDFInteger firstChar, PDFInteger lastChar, std::vector widths, PDFEncoding::Encoding encodingType, encoding::EncodingTable encoding); virtual ~PDFSimpleFont() override = default; virtual PDFRealizedFontPointer getRealizedFont(PDFFontPointer font, PDFReal fontSize) const override; virtual QString getTextUsingEncoding(const QByteArray& byteArray) const override; protected: QByteArray m_name; QByteArray m_baseFont; PDFInteger m_firstChar; PDFInteger m_lastChar; std::vector m_widths; PDFEncoding::Encoding m_encodingType; encoding::EncodingTable m_encoding; }; class PDFType1Font : public PDFSimpleFont { public: explicit PDFType1Font(FontDescriptor fontDescriptor, QByteArray name, QByteArray baseFont, PDFInteger firstChar, PDFInteger lastChar, std::vector widths, PDFEncoding::Encoding encodingType, encoding::EncodingTable encoding, StandardFontType standardFontType); virtual ~PDFType1Font() override = default; virtual FontType getFontType() const override; /// Returns the assigned standard font (or invalid, if font is not standard) StandardFontType getStandardFontType() const { return m_standardFontType; } private: StandardFontType m_standardFontType; ///< Type of the standard font (or invalid, if it is not a standard font) }; class PDFTrueTypeFont : public PDFSimpleFont { public: using PDFSimpleFont::PDFSimpleFont; virtual FontType getFontType() const override; }; /// Font cache which caches both fonts, and realized fonts. Cache has individual limit /// for fonts, and realized fonts. class PDFFontCache { public: inline explicit PDFFontCache(size_t fontCacheLimit, size_t realizedFontCacheLimit) : m_fontCacheLimit(fontCacheLimit), m_realizedFontCacheLimit(realizedFontCacheLimit), m_document(nullptr) { } /// Sets the document to the cache. Whole cache is cleared. /// \param document Document to be setted void setDocument(const PDFDocument* document); /// Retrieves font from the cache. If font can't be accessed or created, /// then exception is thrown. /// \param fontObject Font object PDFFontPointer getFont(const PDFObject& fontObject) const; /// Retrieves realized font from the cache. If realized font can't be accessed or created, /// then exception is thrown. /// \param font Font, which should be realized /// \param size Size of the font (in pixels) PDFRealizedFontPointer getRealizedFont(const PDFFontPointer& font, PDFReal size) const; private: const size_t m_fontCacheLimit; const size_t m_realizedFontCacheLimit; mutable QMutex m_mutex; const PDFDocument* m_document; mutable std::map m_fontCache; mutable std::map, PDFRealizedFontPointer> m_realizedFontCache; }; } // namespace pdf #endif // PDFFONT_H