mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Validation settings, certificate store
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
#include <QDataStream>
|
||||
|
||||
#include <array>
|
||||
|
||||
@@ -139,24 +140,24 @@ PDFSignature PDFSignature::parse(const PDFObjectStorage* storage, PDFObject obje
|
||||
return result;
|
||||
}
|
||||
|
||||
PDFSignatureHandler* PDFSignatureHandler::createHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData)
|
||||
PDFSignatureHandler* PDFSignatureHandler::createHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData, const Parameters& parameters)
|
||||
{
|
||||
Q_ASSERT(signatureField);
|
||||
|
||||
const QByteArray& subfilter = signatureField->getSignature().getSubfilter();
|
||||
if (subfilter == "adbe.pkcs7.detached")
|
||||
{
|
||||
return new PDFSignatureHandler_adbe_pkcs7_detached(signatureField, sourceData);
|
||||
return new PDFSignatureHandler_adbe_pkcs7_detached(signatureField, sourceData, parameters);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<PDFSignatureVerificationResult> PDFSignatureHandler::verifySignatures(const PDFForm& form, const QByteArray& sourceData)
|
||||
std::vector<PDFSignatureVerificationResult> PDFSignatureHandler::verifySignatures(const PDFForm& form, const QByteArray& sourceData, const Parameters& parameters)
|
||||
{
|
||||
std::vector<PDFSignatureVerificationResult> result;
|
||||
|
||||
if (form.isAcroForm() || form.isXFAForm())
|
||||
if (parameters.enableVerification && (form.isAcroForm() || form.isXFAForm()))
|
||||
{
|
||||
std::vector<const PDFFormFieldSignature*> signatureFields;
|
||||
auto getSignatureFields = [&signatureFields](const PDFFormField* field)
|
||||
@@ -173,7 +174,7 @@ std::vector<PDFSignatureVerificationResult> PDFSignatureHandler::verifySignature
|
||||
|
||||
for (const PDFFormFieldSignature* signatureField : signatureFields)
|
||||
{
|
||||
if (const PDFSignatureHandler* signatureHandler = createHandler(signatureField, sourceData))
|
||||
if (const PDFSignatureHandler* signatureHandler = createHandler(signatureField, sourceData, parameters))
|
||||
{
|
||||
result.emplace_back(signatureHandler->verify());
|
||||
delete signatureHandler;
|
||||
@@ -399,7 +400,12 @@ void PDFPublicKeySignatureHandler::verifyCertificate(PDFSignatureVerificationRes
|
||||
break;
|
||||
}
|
||||
|
||||
X509_STORE_CTX_set_flags(context, X509_V_FLAG_TRUSTED_FIRST);
|
||||
unsigned long flags = X509_V_FLAG_TRUSTED_FIRST;
|
||||
if (m_parameters.ignoreExpirationDate)
|
||||
{
|
||||
flags |= X509_V_FLAG_NO_CHECK_TIME;
|
||||
}
|
||||
X509_STORE_CTX_set_flags(context, flags);
|
||||
|
||||
int verificationResult = X509_verify_cert(context);
|
||||
if (verificationResult <= 0)
|
||||
@@ -766,11 +772,47 @@ PDFCertificateInfo PDFPublicKeySignatureHandler::getCertificateInfo(X509* certif
|
||||
|
||||
info.setKeyUsage(static_cast<PDFCertificateInfo::KeyUsageFlags>(keyUsage));
|
||||
}
|
||||
|
||||
unsigned char* buffer = nullptr;
|
||||
int length = i2d_X509(certificate, &buffer);
|
||||
if (length >= 0)
|
||||
{
|
||||
Q_ASSERT(buffer);
|
||||
info.setCertificateData(QByteArray(reinterpret_cast<const char*>(buffer), length));
|
||||
OPENSSL_free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void PDFCertificateInfo::serialize(QDataStream& stream) const
|
||||
{
|
||||
stream << persist_version;
|
||||
stream << m_version;
|
||||
stream << m_keySize;
|
||||
stream << m_publicKey;
|
||||
stream << m_nameEntries;
|
||||
stream << m_notValidBefore;
|
||||
stream << m_notValidAfter;
|
||||
stream << m_keyUsage;
|
||||
stream << m_certificateData;
|
||||
}
|
||||
|
||||
void PDFCertificateInfo::deserialize(QDataStream& stream)
|
||||
{
|
||||
int persist_version = 0;
|
||||
stream >> persist_version;
|
||||
stream >> m_version;
|
||||
stream >> m_keySize;
|
||||
stream >> m_publicKey;
|
||||
stream >> m_nameEntries;
|
||||
stream >> m_notValidBefore;
|
||||
stream >> m_notValidAfter;
|
||||
stream >> m_keyUsage;
|
||||
stream >> m_certificateData;
|
||||
}
|
||||
|
||||
QDateTime PDFCertificateInfo::getNotValidBefore() const
|
||||
{
|
||||
return m_notValidBefore;
|
||||
@@ -791,12 +833,12 @@ void PDFCertificateInfo::setNotValidAfter(const QDateTime& notValidAfter)
|
||||
m_notValidAfter = notValidAfter;
|
||||
}
|
||||
|
||||
long PDFCertificateInfo::getVersion() const
|
||||
int32_t PDFCertificateInfo::getVersion() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
void PDFCertificateInfo::setVersion(long version)
|
||||
void PDFCertificateInfo::setVersion(int32_t version)
|
||||
{
|
||||
m_version = version;
|
||||
}
|
||||
@@ -831,6 +873,46 @@ void PDFCertificateInfo::setKeyUsage(KeyUsageFlags keyUsage)
|
||||
m_keyUsage = keyUsage;
|
||||
}
|
||||
|
||||
std::optional<PDFCertificateInfo> PDFCertificateInfo::getCertificateInfo(const QByteArray& certificateData)
|
||||
{
|
||||
std::optional<PDFCertificateInfo> result;
|
||||
|
||||
PDFOpenSSLGlobalLock lock;
|
||||
const unsigned char* data = reinterpret_cast<const unsigned char*>(certificateData.constData());
|
||||
if (X509* certificate = d2i_X509(nullptr, &data, certificateData.length()))
|
||||
{
|
||||
result = PDFPublicKeySignatureHandler::getCertificateInfo(certificate);
|
||||
X509_free(certificate);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArray PDFCertificateInfo::getCertificateData() const
|
||||
{
|
||||
return m_certificateData;
|
||||
}
|
||||
|
||||
void PDFCertificateInfo::setCertificateData(const QByteArray& certificateData)
|
||||
{
|
||||
m_certificateData = certificateData;
|
||||
}
|
||||
|
||||
void PDFCertificateStore::CertificateEntry::serialize(QDataStream& stream) const
|
||||
{
|
||||
stream << persist_version;
|
||||
stream << type;
|
||||
stream << info;
|
||||
}
|
||||
|
||||
void PDFCertificateStore::CertificateEntry::deserialize(QDataStream& stream)
|
||||
{
|
||||
int persist_version = 0;
|
||||
stream >> persist_version;
|
||||
stream >> type;
|
||||
stream >> info;
|
||||
}
|
||||
|
||||
QString PDFPublicKeySignatureHandler::getStringFromX509Name(X509_NAME* name, int nid)
|
||||
{
|
||||
QString result;
|
||||
@@ -852,7 +934,7 @@ QString PDFPublicKeySignatureHandler::getStringFromX509Name(X509_NAME* name, int
|
||||
return result;
|
||||
}
|
||||
|
||||
QDateTime pdf::PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME* time)
|
||||
QDateTime PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME* time)
|
||||
{
|
||||
QDateTime result;
|
||||
|
||||
@@ -869,6 +951,45 @@ QDateTime pdf::PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME*
|
||||
return result;
|
||||
}
|
||||
|
||||
void PDFCertificateStore::serialize(QDataStream& stream) const
|
||||
{
|
||||
stream << persist_version;
|
||||
stream << m_certificates;
|
||||
}
|
||||
|
||||
void pdf::PDFCertificateStore::deserialize(QDataStream& stream)
|
||||
{
|
||||
int persist_version = 0;
|
||||
stream >> persist_version;
|
||||
stream >> m_certificates;
|
||||
}
|
||||
|
||||
bool PDFCertificateStore::add(EntryType type, const QByteArray& certificate)
|
||||
{
|
||||
if (auto certificateInfo = PDFCertificateInfo::getCertificateInfo(certificate))
|
||||
{
|
||||
return add(type, qMove(*certificateInfo));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PDFCertificateStore::add(EntryType type, PDFCertificateInfo info)
|
||||
{
|
||||
auto it = std::find_if(m_certificates.cbegin(), m_certificates.cend(), [&info](const auto& entry) { return entry.info == info; });
|
||||
if (it == m_certificates.cend())
|
||||
{
|
||||
m_certificates.push_back({ type, qMove(info) });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pdf::PDFCertificateStore::contains(const pdf::PDFCertificateInfo& info)
|
||||
{
|
||||
return std::find_if(m_certificates.cbegin(), m_certificates.cend(), [&info](const auto& entry) { return entry.info == info; }) != m_certificates.cend();
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -879,23 +1000,42 @@ QDateTime pdf::PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME*
|
||||
|
||||
void pdf::PDFPublicKeySignatureHandler::addTrustedCertificates(X509_STORE* store) const
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
HCERTSTORE certStore = CertOpenSystemStore(NULL, L"ROOT");
|
||||
PCCERT_CONTEXT context = nullptr;
|
||||
if (certStore)
|
||||
if (m_parameters.store)
|
||||
{
|
||||
while (context = CertEnumCertificatesInStore(certStore, context))
|
||||
const PDFCertificateStore::CertificateEntries& certificates = m_parameters.store->getCertificates();
|
||||
for (const auto& entry : certificates)
|
||||
{
|
||||
const unsigned char* pointer = context->pbCertEncoded;
|
||||
X509* certificate = d2i_X509(nullptr, &pointer, context->cbCertEncoded);
|
||||
QByteArray certificateData = entry.info.getCertificateData();
|
||||
const unsigned char* pointer = reinterpret_cast<const unsigned char*>(certificateData.constData());
|
||||
X509* certificate = d2i_X509(nullptr, &pointer, certificateData.length());
|
||||
if (certificate)
|
||||
{
|
||||
X509_STORE_add_cert(store, certificate);
|
||||
X509_free(certificate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
||||
#ifdef Q_OS_WIN
|
||||
if (m_parameters.useSystemCertificateStore)
|
||||
{
|
||||
HCERTSTORE certStore = CertOpenSystemStore(NULL, L"ROOT");
|
||||
PCCERT_CONTEXT context = nullptr;
|
||||
if (certStore)
|
||||
{
|
||||
while (context = CertEnumCertificatesInStore(certStore, context))
|
||||
{
|
||||
const unsigned char* pointer = context->pbCertEncoded;
|
||||
X509* certificate = d2i_X509(nullptr, &pointer, context->cbCertEncoded);
|
||||
if (certificate)
|
||||
{
|
||||
X509_STORE_add_cert(store, certificate);
|
||||
X509_free(certificate);
|
||||
}
|
||||
}
|
||||
|
||||
CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@@ -26,10 +26,13 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
class QDataStream;
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFForm;
|
||||
class PDFObjectStorage;
|
||||
class PDFCertificateStore;
|
||||
class PDFFormFieldSignature;
|
||||
|
||||
/// Signature reference dictionary.
|
||||
@@ -153,6 +156,15 @@ class PDFFORQTLIBSHARED_EXPORT PDFCertificateInfo
|
||||
public:
|
||||
explicit inline PDFCertificateInfo() = default;
|
||||
|
||||
void serialize(QDataStream& stream) const;
|
||||
void deserialize(QDataStream& stream);
|
||||
|
||||
friend inline QDataStream& operator<<(QDataStream& stream, const PDFCertificateInfo& info) { info.serialize(stream); return stream; }
|
||||
friend inline QDataStream& operator>>(QDataStream& stream, PDFCertificateInfo& info) { info.deserialize(stream); return stream; }
|
||||
|
||||
bool operator ==(const PDFCertificateInfo&) const = default;
|
||||
bool operator !=(const PDFCertificateInfo&) const = default;
|
||||
|
||||
/// These entries are taken from RFC 5280, section 4.1.2.4,
|
||||
/// they are supported and loaded from the certificate, with
|
||||
/// exception of Email entry.
|
||||
@@ -210,8 +222,8 @@ public:
|
||||
QDateTime getNotValidAfter() const;
|
||||
void setNotValidAfter(const QDateTime& notValidAfter);
|
||||
|
||||
long getVersion() const;
|
||||
void setVersion(long version);
|
||||
int32_t getVersion() const;
|
||||
void setVersion(int32_t version);
|
||||
|
||||
PublicKey getPublicKey() const;
|
||||
void setPublicKey(const PublicKey& publicKey);
|
||||
@@ -222,14 +234,25 @@ public:
|
||||
KeyUsageFlags getKeyUsage() const;
|
||||
void setKeyUsage(KeyUsageFlags keyUsage);
|
||||
|
||||
QByteArray getCertificateData() const;
|
||||
void setCertificateData(const QByteArray& certificateData);
|
||||
|
||||
/// Creates certificate info from binary data. Binary data must
|
||||
/// contain DER-encoded certificate.
|
||||
/// \param certificateData Data
|
||||
static std::optional<PDFCertificateInfo> getCertificateInfo(const QByteArray& certificateData);
|
||||
|
||||
private:
|
||||
long m_version = 0;
|
||||
static constexpr int persist_version = 1;
|
||||
|
||||
int32_t m_version = 0;
|
||||
int m_keySize = 0;
|
||||
PublicKey m_publicKey = KeyUnknown;
|
||||
std::array<QString, NameEnd> m_nameEntries;
|
||||
QDateTime m_notValidBefore;
|
||||
QDateTime m_notValidAfter;
|
||||
KeyUsageFlags m_keyUsage;
|
||||
QByteArray m_certificateData;
|
||||
};
|
||||
|
||||
using PDFCertificateInfos = std::vector<PDFCertificateInfo>;
|
||||
@@ -349,11 +372,20 @@ public:
|
||||
|
||||
virtual PDFSignatureVerificationResult verify() const = 0;
|
||||
|
||||
struct Parameters
|
||||
{
|
||||
const PDFCertificateStore* store = nullptr;
|
||||
bool enableVerification = true;
|
||||
bool ignoreExpirationDate = false;
|
||||
bool useSystemCertificateStore = true;
|
||||
};
|
||||
|
||||
/// Tries to verify all signatures in the form. If form is invalid, then
|
||||
/// empty vector is returned.
|
||||
/// \param form Form
|
||||
/// \param sourceData Source data
|
||||
static std::vector<PDFSignatureVerificationResult> verifySignatures(const PDFForm& form, const QByteArray& sourceData);
|
||||
/// \param parameters Verification settings
|
||||
static std::vector<PDFSignatureVerificationResult> verifySignatures(const PDFForm& form, const QByteArray& sourceData, const Parameters& parameters);
|
||||
|
||||
private:
|
||||
|
||||
@@ -361,7 +393,71 @@ private:
|
||||
/// If signature format is unknown, then nullptr is returned.
|
||||
/// \param signatureField Signature field
|
||||
/// \param sourceData
|
||||
static PDFSignatureHandler* createHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData);
|
||||
/// \param parameters Verification settings
|
||||
static PDFSignatureHandler* createHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData, const Parameters& parameters);
|
||||
};
|
||||
|
||||
/// Trusted certificate store. Contains list of trusted certificates. Store
|
||||
/// can be persisted to the persistent storage trough serialization/deserialization.
|
||||
/// Persisting method is versioned.
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFCertificateStore
|
||||
{
|
||||
public:
|
||||
explicit inline PDFCertificateStore() = default;
|
||||
|
||||
void serialize(QDataStream& stream) const;
|
||||
void deserialize(QDataStream& stream);
|
||||
|
||||
enum class EntryType : int
|
||||
{
|
||||
User, ///< Certificate has been added manually by the user
|
||||
EUTL ///< Certificate comes EU trusted list
|
||||
};
|
||||
|
||||
struct CertificateEntry
|
||||
{
|
||||
void serialize(QDataStream& stream) const;
|
||||
void deserialize(QDataStream& stream);
|
||||
|
||||
friend inline QDataStream& operator<<(QDataStream& stream, const CertificateEntry& entry) { entry.serialize(stream); return stream; }
|
||||
friend inline QDataStream& operator>>(QDataStream& stream, CertificateEntry& entry) { entry.deserialize(stream); return stream; }
|
||||
|
||||
static constexpr int persist_version = 1;
|
||||
EntryType type = EntryType::User;
|
||||
PDFCertificateInfo info;
|
||||
};
|
||||
|
||||
using CertificateEntries = std::vector<CertificateEntry>;
|
||||
|
||||
/// Tries to add new certificate to the certificate store. If certificate
|
||||
/// is already here, then nothing happens and function returns true.
|
||||
/// Otherwise data are checked, if they really contains a certificate,
|
||||
/// and if yes, then it is added. Function returns false if, and only if,
|
||||
/// error occured and certificate was not added.
|
||||
/// \param type Type
|
||||
/// \param certificate Certificate
|
||||
bool add(EntryType type, const QByteArray& certificate);
|
||||
|
||||
/// Tries to add new certificate to the certificate store. If certificate
|
||||
/// is already here, then nothing happens and function returns true.
|
||||
/// Otherwise data are checked, if they really contains a certificate,
|
||||
/// and if yes, then it is added. Function returns false if, and only if,
|
||||
/// error occured and certificate was not added.
|
||||
/// \param type Type
|
||||
/// \param info Certificate info
|
||||
bool add(EntryType type, PDFCertificateInfo info);
|
||||
|
||||
/// Returns true, if storage contains given certificate
|
||||
/// \param info Certificate info
|
||||
bool contains(const PDFCertificateInfo& info);
|
||||
|
||||
/// Get certificates stored in the store
|
||||
const CertificateEntries& getCertificates() const { return m_certificates; }
|
||||
|
||||
private:
|
||||
static constexpr int persist_version = 1;
|
||||
|
||||
CertificateEntries m_certificates;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
@@ -31,9 +31,10 @@ namespace pdf
|
||||
class PDFPublicKeySignatureHandler : public PDFSignatureHandler
|
||||
{
|
||||
protected:
|
||||
explicit PDFPublicKeySignatureHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData) :
|
||||
explicit PDFPublicKeySignatureHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData, const Parameters& parameters) :
|
||||
m_signatureField(signatureField),
|
||||
m_sourceData(sourceData)
|
||||
m_sourceData(sourceData),
|
||||
m_parameters(parameters)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -45,6 +46,7 @@ protected:
|
||||
|
||||
BIO* getSignedDataBuffer(PDFSignatureVerificationResult& result, QByteArray& outputBuffer) const;
|
||||
|
||||
public:
|
||||
/// Return a list of certificates from PKCS7 object
|
||||
static STACK_OF(X509)* getCertificates(PKCS7* pkcs7);
|
||||
|
||||
@@ -61,13 +63,14 @@ protected:
|
||||
protected:
|
||||
const PDFFormFieldSignature* m_signatureField;
|
||||
QByteArray m_sourceData;
|
||||
Parameters m_parameters;
|
||||
};
|
||||
|
||||
class PDFSignatureHandler_adbe_pkcs7_detached : public PDFPublicKeySignatureHandler
|
||||
{
|
||||
public:
|
||||
explicit PDFSignatureHandler_adbe_pkcs7_detached(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData) :
|
||||
PDFPublicKeySignatureHandler(signatureField, sourceData)
|
||||
explicit PDFSignatureHandler_adbe_pkcs7_detached(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData, const Parameters& parameters) :
|
||||
PDFPublicKeySignatureHandler(signatureField, sourceData, parameters)
|
||||
{
|
||||
|
||||
}
|
||||
|
@@ -248,55 +248,6 @@ size_t PDFTextCharacterSpatialIndex::queryImpl(size_t nodeIndex, const QRectF& r
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QDataStream& operator>>(QDataStream& stream, std::vector<T>& vector)
|
||||
{
|
||||
std::vector<T>::size_type size = 0;
|
||||
stream >> size;
|
||||
vector.resize(size);
|
||||
for (T& item : vector)
|
||||
{
|
||||
stream >> item;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QDataStream& operator<<(QDataStream& stream, const std::vector<T>& vector)
|
||||
{
|
||||
stream << vector.size();
|
||||
for (const T& item : vector)
|
||||
{
|
||||
stream << item;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QDataStream& operator>>(QDataStream& stream, std::set<T>& set)
|
||||
{
|
||||
std::set<T>::size_type size = 0;
|
||||
stream >> size;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
T item;
|
||||
stream >> item;
|
||||
set.insert(set.end(), qMove(item));
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QDataStream& operator<<(QDataStream& stream, const std::set<T>& set)
|
||||
{
|
||||
stream << set.size();
|
||||
for (const T& item : set)
|
||||
{
|
||||
stream << item;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
PDFTextLayout::PDFTextLayout()
|
||||
{
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include <QByteArray>
|
||||
#include <QDataStream>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
#include <functional>
|
||||
@@ -567,6 +568,94 @@ private:
|
||||
std::vector<ClosedInterval> m_intervals;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
QDataStream& operator>>(QDataStream& stream, std::vector<T>& vector)
|
||||
{
|
||||
std::vector<T>::size_type size = 0;
|
||||
stream >> size;
|
||||
vector.resize(size);
|
||||
for (T& item : vector)
|
||||
{
|
||||
stream >> item;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QDataStream& operator<<(QDataStream& stream, const std::vector<T>& vector)
|
||||
{
|
||||
stream << vector.size();
|
||||
for (const T& item : vector)
|
||||
{
|
||||
stream << item;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T, size_t Size>
|
||||
QDataStream& operator>>(QDataStream& stream, std::array<T, Size>& array)
|
||||
{
|
||||
std::array<T, Size>::size_type size = 0;
|
||||
stream >> size;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
if (i < array.size())
|
||||
{
|
||||
stream >> array[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
T item;
|
||||
stream >> item;
|
||||
}
|
||||
}
|
||||
|
||||
// If array size was changed, then fill in empty objects
|
||||
for (size_t i = size; i < array.size(); ++i)
|
||||
{
|
||||
array[i] = T();
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T, size_t Size>
|
||||
QDataStream& operator<<(QDataStream& stream, const std::array<T, Size>& array)
|
||||
{
|
||||
stream << array.size();
|
||||
for (const T& item : array)
|
||||
{
|
||||
stream << item;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QDataStream& operator>>(QDataStream& stream, std::set<T>& set)
|
||||
{
|
||||
std::set<T>::size_type size = 0;
|
||||
stream >> size;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
T item;
|
||||
stream >> item;
|
||||
set.insert(set.end(), qMove(item));
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QDataStream& operator<<(QDataStream& stream, const std::set<T>& set)
|
||||
{
|
||||
stream << set.size();
|
||||
for (const T& item : set)
|
||||
{
|
||||
stream << item;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFUTILS_H
|
||||
|
Reference in New Issue
Block a user