// SPDX-FileCopyrightText: 2005 Dominik Seichter <domseichter@web.de>
// SPDX-FileCopyrightText: 2020 Francesco Pretto <ceztko@gmail.com>
// SPDX-License-Identifier: LGPL-2.0-or-later OR MPL-2.0

#ifndef PDF_FONT_METRICS_H
#define PDF_FONT_METRICS_H

#include "PdfString.h"
#include "PdfCMapEncoding.h"
#include "PdfCIDToGIDMap.h"
#include <podofo/auxiliary/Matrix.h>
#include <podofo/auxiliary/Corners.h>

FORWARD_DECLARE_FREETYPE();

namespace PoDoFo {

class PdfFontMetrics;

// NOTE: Underlying type may change in the future
using GlyphMetricsListConstPtr = std::shared_ptr<const std::vector<double>>;

/// Convenience typedef for a const PdfEncoding shared ptr
using PdfFontMetricsConstPtr = std::shared_ptr<const PdfFontMetrics>;

/// This abstract class provides access to font metrics information.
///
/// The class doesn't know anything about CIDs (Character IDs),
/// it just index glyphs, or GIDs where the terminology applies
class PODOFO_API PdfFontMetrics
{
    friend class PdfFont;
    friend class PdfFontSimple;
    friend class PdfFontObject;
    friend class PdfFontManager;
    friend class PdfFontMetricsBase;
    friend class PdfFontMetricsFreetype;
    friend class PdfEncodingFactory;
    friend class PdfEncodingMapSimple;
    friend class PdfDifferenceEncoding;
    PODOFO_PRIVATE_FRIEND(class FontTrueTypeSubset);

private:
    PdfFontMetrics();

public:
    virtual ~PdfFontMetrics();

    static std::unique_ptr<const PdfFontMetrics> Create(const std::string_view& filepath, unsigned faceIndex = 0);

    static std::unique_ptr<const PdfFontMetrics> CreateFromBuffer(const bufferview& buffer, unsigned faceIndex = 0);

    /// Get the glyph count
    /// @remarks By defaults tt returns the actual number of glyphs in the font program
    unsigned GetGlyphCount() const;

    /// Get the glyph count with the given glyph access
    /// @param access the desired access for retrieving the glyph count
    unsigned GetGlyphCount(PdfGlyphAccess access) const;

    /// Get the width of a single glyph id. It tries to access parsed pdf metrics first,
    /// and if unavailable it retrieve it from available font program
    ///
    /// @param gid id of the glyph
    /// @returns the width of a single glyph id
    double GetGlyphWidth(unsigned gid) const;
    bool TryGetGlyphWidth(unsigned gid, double& width) const;

    /// Get the width of a single glyph id
    ///
    /// @param gid id of the glyph
    /// @param access the desired access for retrieving the metrics
    /// @returns the width of a single glyph id
    double GetGlyphWidth(unsigned gid, PdfGlyphAccess access) const;
    bool TryGetGlyphWidth(unsigned gid, PdfGlyphAccess access, double& width) const;

    /// Some fonts provides a glyph substitution list, eg. for ligatures.
    /// OpenType fonts for example provides GSUB "Glyph Substitution Table"
    /// @param gids gids to be substituted
    /// @param backwardMap list of gid counts to remap back substituted gids
    ///     eg. { 32, 102, 105 } gets substituted in { 32, 174 }
    ///     the backward map is { 1, 2 }
    virtual void SubstituteGIDs(std::vector<unsigned>& gids,
        std::vector<unsigned char>& backwardMap) const;

    /// Determines if the metrics has a valid Unicode
    /// code point to gid map
    virtual bool HasUnicodeMapping() const = 0;

    /// Try to retrieve the mapped gid from Unicode code point
    /// @remarks don't use this method directly unless you know
    /// what you're doing: use PdfFont::TryGetGID instead
    virtual bool TryGetGID(char32_t codePoint, unsigned& gid) const = 0;

    /// Retrieve the line spacing for this font
    /// @returns the linespacing in PDF units
    virtual double GetLineSpacing() const = 0;

    /// Get the width of the underline for the current
    /// font size in PDF units
    /// @returns the thickness of the underline in PDF units
    virtual double GetUnderlineThickness() const = 0;

    /// Return the position of the underline for the current font
    /// size in PDF units
    /// @returns the underline position in PDF units
    virtual double GetUnderlinePosition() const = 0;

    /// Return the position of the strikethrough for the current font
    /// size in PDF units
    /// @returns the strikethrough position in PDF units
    virtual double GetStrikeThroughPosition() const = 0;

    /// Get the width of the strikethrough for the current
    /// font size in PDF units
    /// @returns the thickness of the strikethrough in PDF units
    virtual double GetStrikeThroughThickness() const = 0;

    virtual PdfFontFileType GetFontFileType() const = 0;

    bool HasFontFileData() const;

    /// Get an actual font data view
    ///
    /// The data shall be resident. For font coming from the /FontFile
    /// keys, GetFontFileObject() may also be available.
    /// @returns a binary buffer of data containing the font data
    bufferview GetOrLoadFontFileData() const;

    /// Get the actual font file object from a /FontFile like key, if available
    ///
    /// For font data coming from a file imported font, see GetFontFileData()
    /// @returns a binary buffer of data containing the font data
    virtual const PdfObject* GetFontFileObject() const;

    /// Get /Length1 value for the font file, if available
    virtual unsigned GetFontFileLength1() const = 0;

    /// Get /Length2 value for the font file, if available
    virtual unsigned GetFontFileLength2() const = 0;

    /// Get /Length3 value for the font file, if available
    virtual unsigned GetFontFileLength3() const = 0;

    /// Get the actual /FontName, eg. "AAAAAA+Arial,Bold", if available
    ///
    /// By default returns empty string
    /// @returns the postscript name of the font or empty string if no postscript name is available.
    virtual std::string_view GetFontName() const = 0;

    /// Get the actual /FontName, eg. "AAAAAA+Arial,Bold", if available
    /// By default returns GetFontName()
    virtual std::string_view GetFontNameRaw() const;

    /// Get the actual /FontFamily, eg. "Times", if available
    virtual std::string_view GetFontFamilyName() const = 0;

    /// Get a family font name, either from /FontFamily or constructed
    /// from available /BaseFont, /FontName (eg. "AAAAAA+Arial,Bold" becomes "Arial")
    ///
    /// @remarks It doesn't correspond to /BaseFont name entry in the font
    std::string_view GeFontFamilyNameSafe() const;

    /// Get the length of the subset prefix (eg. 7 for "AAAAAA+") if present
    virtual unsigned char GetSubsetPrefixLength() const;

    /// Get a an approximate PostScript name, from available /BaseFont, /FontName
    /// (eg. "AAAAAA+Arial-Bold" becomes "Arial-Bold")
    /// By default returns GetFontName()
    std::string_view GetPostScriptNameRough() const;

    virtual PdfFontStretch GetFontStretch() const = 0;

    /// Get the weight of this font.
    /// @returns the weight of this font (400 <= x < 700 means normal, x >= 700 means bold)
    unsigned GetWeight() const;
    virtual int GetWeightRaw() const = 0;

    virtual bool TryGetFlags(PdfFontDescriptorFlags& value) const = 0;
    PdfFontDescriptorFlags GetFlags() const;

    /// Create the bounding box vector in PDF units
    ///
    /// @param value write the bounding box to this vector
    virtual bool TryGetBoundingBox(Corners& value) const = 0;
    Corners GetBoundingBox() const;

    /// Get the italic angle of this font.
    /// Used to build the font dictionary
    /// @returns the italic angle of this font.
    virtual bool TryGetItalicAngle(double& value) const = 0;
    double GetItalicAngle() const;

    /// Get the ascent of this font in PDF
    /// units for the current font size.
    ///
    /// @returns the ascender for this font
    ///
    /// @see GetAscent
    virtual bool TryGetAscent(double& value) const = 0;
    double GetAscent() const;

    /// Get the descent of this font in PDF
    /// units for the current font size.
    /// This value is usually negative!
    ///
    /// @returns the descender for this font
    ///
    /// @see GetDescent
    virtual bool TryGetDescent(double& value) const = 0;
    double GetDescent() const;

    /// The vertical coordinate of the top of flat capital letters, measured from the baseline
    virtual bool TryGetCapHeight(double& value) const = 0;
    double GetCapHeight() const;

    /// The thickness, measured horizontally, of the dominant vertical stems of glyphs in the font
    virtual bool TryGetStemV(double& value) const = 0;
    double GetStemV() const;

    /* /Leading (optional, default 0)
     */
    double GetLeading() const;
    virtual double GetLeadingRaw() const = 0;

    /// The font’s x height: the vertical coordinate of the top of flat nonascending
    /// lowercase letters (like the letter x), measured from the baseline, in
    /// fonts that have Latin characters (optional, default 0)
    double GetXHeight() const;
    virtual double GetXHeightRaw() const = 0;

    /// The thickness, measured vertically, of the dominant horizontal
    /// stems of glyphs in the font (optional, default 0)
    double GetStemH() const;
    virtual double GetStemHRaw() const = 0;

    /// /AvgWidth (optional, default 0)
    double GetAvgWidth() const;
    virtual double GetAvgWidthRaw() const = 0;

    /// /MaxWidth (optional, default 0)
    double GetMaxWidth() const;
    virtual double GetMaxWidthRaw() const = 0;

    /// /MissingWidth or /DW in CID fonts (optional default 1000 in CID fonts, 0 otherwise)
    double GetDefaultWidth() const;
    virtual double GetDefaultWidthRaw() const = 0;

    /// Get whether the font style is bold.
    ///
    /// This is a logical value that can be inferred
    /// from several characteristics
    PdfFontStyle GetStyle() const;

    virtual bool IsObjectLoaded() const;

    bool IsStandard14FontMetrics() const;

    virtual bool IsStandard14FontMetrics(PdfStandard14FontType& std14Font) const;

    /// Returns the matrix mapping glyph space to text space
    virtual const Matrix& GetMatrix() const;

    /// Determine if the metrics are for Adobe Type1 like font
    bool IsType1Kind() const;

    /// Determine if the metrics are TrueType like font
    bool IsTrueTypeKind() const;

    /// Determine if the font is non symbolic according to the PDF definition
    ///
    /// The font is symbolic if "contains glyphs outside the Standard Latin character set"
    bool IsPdfSymbolic() const;

    /// Determine if the font is symbolic according to the PDF definition
    ///
    /// The font is symbolic if "uses the Standard Latin character set or a subset of it."
    bool IsPdfNonSymbolic() const;

    /// Create a best effort /ToUnicode map based on the
    /// character unicode maps of the font
    ///
    /// This is implemented just for PdfFontMetricsFreetype
    /// This map may be unreliable because of ligatures,
    /// other kind of character substitutions, or glyphs
    /// mapping to multiple unicode codepoints.
    virtual std::unique_ptr<PdfCMapEncoding> CreateToUnicodeMap(const PdfEncodingLimits& limitHints) const;

#ifdef PODOFO_3RDPARTY_INTEROP_ENABLED
    virtual FT_Face GetFaceHandle() const = 0;
#endif // PODOFO_3RDPARTY_INTEROP_ENABLED

public:
    const std::string& GetFilePath() const { return m_FilePath; }
    unsigned GetFaceIndex() const { return m_FaceIndex; }

protected:
    virtual PdfFontType GetFontType() const;

    /// Get a semantical base name for the font that can be used to
    /// compose the final name, eg. from "AAAAAA+Arial,Bold" to "Arial"
    ///
    /// @remarks It doesn't correspond to /BaseFont name entry in the font
    virtual std::string_view GetBaseFontName() const = 0;

    virtual bool getIsBoldHint() const = 0;
    virtual bool getIsItalicHint() const = 0;
    virtual const datahandle& GetFontFileDataHandle() const = 0;

    virtual unsigned GetGlyphCountFontProgram() const;
    virtual bool TryGetGlyphWidthFontProgram(unsigned gid, double& width) const;
    virtual void ExportType3GlyphData(PdfDictionary& fontDict, cspan<std::string_view> glyphs) const;

    bool HasParsedWidths() const;

    unsigned GetParsedWidthsCount() const;

    /// Retrieve the parsed width from a /W or /Widths entry, if available
    GlyphMetricsListConstPtr GetParsedWidths() const { return m_ParsedWidths; }

    void SetParsedWidths(GlyphMetricsListConstPtr&& parsedWidths);

private:
    static std::unique_ptr<const PdfFontMetrics> CreateFromFile(const std::string_view& filepath, unsigned faceIndex,
        const PdfFontMetrics* metrics, bool skipNormalization);

    static std::unique_ptr<const PdfFontMetrics> CreateFromBuffer(const bufferview& buffer, unsigned faceIndex,
        const PdfFontMetrics* metrics, bool skipNormalization);

    static std::unique_ptr<PdfFontMetrics> CreateFromFace(FT_Face face, std::unique_ptr<charbuff>&& buffer,
        const PdfFontMetrics* metrics, bool skipNormalization);

    /// Create a new font metrics by merging characteristics from this instance
    std::unique_ptr<const PdfFontMetrics> CreateMergedMetrics(bool skipNormalization) const;

    /// Get an implicit encoding, such as the one of standard14 fonts,
    /// or the built-in encoding of a Type1/TrueType font
    PdfEncodingMapConstPtr GetDefaultEncoding(PdfCIDToGIDMapConstPtr& cidToGidMap) const;
    PdfEncodingMapConstPtr GetDefaultEncoding() const;

    /// Get a built-in CID to GID map for /TrueType fonts, such as when no /Encoding is defined
    PdfCIDToGIDMapConstPtr GetTrueTypeBuiltinCIDToGIDMap() const;

    PdfEncodingMapConstPtr getFontType1BuiltInEncoding(FT_Face face) const;
    void initFamilyFontNameSafe();
    PdfEncodingMapConstPtr getDefaultEncoding(bool tryFetchCidToGidMap, PdfCIDToGIDMapConstPtr& cidToGidMap) const;

private:
    PdfFontMetrics(const PdfFontMetrics& rhs) = delete;
    PdfFontMetrics& operator=(const PdfFontMetrics& rhs) = delete;

private:
    std::string m_FilePath;
    std::string m_FamilyFontNameSafe;
    GlyphMetricsListConstPtr m_ParsedWidths;
    nullable<PdfFontStyle> m_Style;
    unsigned m_FaceIndex;
};

class PODOFO_API PdfFontMetricsBase : public PdfFontMetrics
{
    friend class PdfFontMetricsStandard14;
    friend class PdfFontMetricsObject;

private:
    PdfFontMetricsBase();

public:
    ~PdfFontMetricsBase();

protected:
    const datahandle& GetFontFileDataHandle() const override final;
    FT_Face GetFaceHandle() const override final;
    virtual datahandle getFontFileDataHandle() const = 0;

private:
    bool m_dataInit;
    bool m_faceInit;
    datahandle m_Data;
    FT_Face m_Face;
};



};

#endif // PDF_FONT_METRICS_H
