aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/main.yml5
-rwxr-xr-xbuild-scripts/build-mac7
-rw-r--r--include/qpdf/JSON.hh28
-rw-r--r--include/qpdf/QIntC.hh132
-rw-r--r--include/qpdf/QUtil.hh6
-rw-r--r--libqpdf/JSON.cc130
-rw-r--r--libqpdf/QPDFTokenizer.cc8
-rw-r--r--libqpdf/QPDF_Name.cc5
-rw-r--r--manual/linearization.rst2
9 files changed, 203 insertions, 120 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index aec50b2c..d64b7996 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -90,11 +90,6 @@ jobs:
needs: Prebuild
steps:
- uses: actions/checkout@v3
- - name: 'Download external libs'
- uses: actions/download-artifact@v3
- with:
- name: external-libs
- path: .
- name: 'Mac build and test'
run: build-scripts/build-mac
AppImage:
diff --git a/build-scripts/build-mac b/build-scripts/build-mac
index 094de58f..89e2adf4 100755
--- a/build-scripts/build-mac
+++ b/build-scripts/build-mac
@@ -1,13 +1,6 @@
#!/bin/bash
set -ex
cd $(dirname $0)/..
-unzip qpdf-external-libs-src.zip
-tar xzf external-libs-src/jpegsrc*
-cd jpeg-*
-./configure
-make -k
-sudo make install
-cd ..
cmake -S . -B build -DCI_MODE=1 -DCMAKE_BUILD_TYPE=Release
cmake --build build --verbose -j$(sysctl -n hw.ncpu) -- -k
(cd build; ctest --verbose)
diff --git a/include/qpdf/JSON.hh b/include/qpdf/JSON.hh
index 4b829017..2906d85a 100644
--- a/include/qpdf/JSON.hh
+++ b/include/qpdf/JSON.hh
@@ -340,13 +340,33 @@ class JSON
static void
writeClose(Pipeline* p, bool first, size_t depth, char const* delimeter);
+ enum value_type_e {
+ vt_none,
+ vt_dictionary,
+ vt_array,
+ vt_string,
+ vt_number,
+ vt_bool,
+ vt_null,
+ vt_blob,
+ };
+
struct JSON_value
{
+ JSON_value(value_type_e type_code) :
+ type_code(type_code)
+ {
+ }
virtual ~JSON_value() = default;
virtual void write(Pipeline*, size_t depth) const = 0;
+ const value_type_e type_code{vt_none};
};
struct JSON_dictionary: public JSON_value
{
+ JSON_dictionary() :
+ JSON_value(vt_dictionary)
+ {
+ }
virtual ~JSON_dictionary() = default;
virtual void write(Pipeline*, size_t depth) const;
std::map<std::string, std::shared_ptr<JSON_value>> members;
@@ -354,6 +374,10 @@ class JSON
};
struct JSON_array: public JSON_value
{
+ JSON_array() :
+ JSON_value(vt_array)
+ {
+ }
virtual ~JSON_array() = default;
virtual void write(Pipeline*, size_t depth) const;
std::vector<std::shared_ptr<JSON_value>> elements;
@@ -384,6 +408,10 @@ class JSON
};
struct JSON_null: public JSON_value
{
+ JSON_null() :
+ JSON_value(vt_null)
+ {
+ }
virtual ~JSON_null() = default;
virtual void write(Pipeline*, size_t depth) const;
};
diff --git a/include/qpdf/QIntC.hh b/include/qpdf/QIntC.hh
index dadc7582..b896a026 100644
--- a/include/qpdf/QIntC.hh
+++ b/include/qpdf/QIntC.hh
@@ -63,48 +63,60 @@ namespace QIntC // QIntC = qpdf Integer Conversion
class IntConverter<From, To, false, false>
{
public:
- static To
+ inline static To
convert(From const& i)
{
// From and To are both unsigned.
if (i > std::numeric_limits<To>::max()) {
- std::ostringstream msg;
- msg.imbue(std::locale::classic());
- msg << "integer out of range converting " << i << " from a "
- << sizeof(From) << "-byte unsigned type to a " << sizeof(To)
- << "-byte unsigned type";
- throw std::range_error(msg.str());
+ error(i);
}
return static_cast<To>(i);
}
+
+ static void
+ error(From i)
+ {
+ std::ostringstream msg;
+ msg.imbue(std::locale::classic());
+ msg << "integer out of range converting " << i << " from a "
+ << sizeof(From) << "-byte unsigned type to a " << sizeof(To)
+ << "-byte unsigned type";
+ throw std::range_error(msg.str());
+ }
};
template <typename From, typename To>
class IntConverter<From, To, true, true>
{
public:
- static To
+ inline static To
convert(From const& i)
{
// From and To are both signed.
if ((i < std::numeric_limits<To>::min()) ||
(i > std::numeric_limits<To>::max())) {
- std::ostringstream msg;
- msg.imbue(std::locale::classic());
- msg << "integer out of range converting " << i << " from a "
- << sizeof(From) << "-byte signed type to a " << sizeof(To)
- << "-byte signed type";
- throw std::range_error(msg.str());
+ error(i);
}
return static_cast<To>(i);
}
+
+ static void
+ error(From i)
+ {
+ std::ostringstream msg;
+ msg.imbue(std::locale::classic());
+ msg << "integer out of range converting " << i << " from a "
+ << sizeof(From) << "-byte signed type to a " << sizeof(To)
+ << "-byte signed type";
+ throw std::range_error(msg.str());
+ }
};
template <typename From, typename To>
class IntConverter<From, To, true, false>
{
public:
- static To
+ inline static To
convert(From const& i)
{
// From is signed, and To is unsigned. If i > 0, it's safe to
@@ -112,22 +124,28 @@ namespace QIntC // QIntC = qpdf Integer Conversion
// compare with To's max.
auto ii = static_cast<typename to_u<From>::type>(i);
if ((i < 0) || (ii > std::numeric_limits<To>::max())) {
- std::ostringstream msg;
- msg.imbue(std::locale::classic());
- msg << "integer out of range converting " << i << " from a "
- << sizeof(From) << "-byte signed type to a " << sizeof(To)
- << "-byte unsigned type";
- throw std::range_error(msg.str());
+ error(i);
}
return static_cast<To>(i);
}
+
+ static void
+ error(From i)
+ {
+ std::ostringstream msg;
+ msg.imbue(std::locale::classic());
+ msg << "integer out of range converting " << i << " from a "
+ << sizeof(From) << "-byte signed type to a " << sizeof(To)
+ << "-byte unsigned type";
+ throw std::range_error(msg.str());
+ }
};
template <typename From, typename To>
class IntConverter<From, To, false, true>
{
public:
- static To
+ inline static To
convert(From const& i)
{
// From is unsigned, and to is signed. Convert To's max to the
@@ -135,98 +153,104 @@ namespace QIntC // QIntC = qpdf Integer Conversion
auto maxval = static_cast<typename to_u<To>::type>(
std::numeric_limits<To>::max());
if (i > maxval) {
- std::ostringstream msg;
- msg.imbue(std::locale::classic());
- msg << "integer out of range converting " << i << " from a "
- << sizeof(From) << "-byte unsigned type to a " << sizeof(To)
- << "-byte signed type";
- throw std::range_error(msg.str());
+ error(i);
}
return static_cast<To>(i);
}
+
+ static void
+ error(From i)
+ {
+ std::ostringstream msg;
+ msg.imbue(std::locale::classic());
+ msg << "integer out of range converting " << i << " from a "
+ << sizeof(From) << "-byte unsigned type to a " << sizeof(To)
+ << "-byte signed type";
+ throw std::range_error(msg.str());
+ }
};
// Specific converters. The return type of each function must match
// the second template parameter to IntConverter.
template <typename T>
- char
+ inline char
to_char(T const& i)
{
return IntConverter<T, char>::convert(i);
}
template <typename T>
- unsigned char
+ inline unsigned char
to_uchar(T const& i)
{
return IntConverter<T, unsigned char>::convert(i);
}
template <typename T>
- short
+ inline short
to_short(T const& i)
{
return IntConverter<T, short>::convert(i);
}
template <typename T>
- unsigned short
+ inline unsigned short
to_ushort(T const& i)
{
return IntConverter<T, unsigned short>::convert(i);
}
template <typename T>
- int
+ inline int
to_int(T const& i)
{
return IntConverter<T, int>::convert(i);
}
template <typename T>
- unsigned int
+ inline unsigned int
to_uint(T const& i)
{
return IntConverter<T, unsigned int>::convert(i);
}
template <typename T>
- size_t
+ inline size_t
to_size(T const& i)
{
return IntConverter<T, size_t>::convert(i);
}
template <typename T>
- qpdf_offset_t
+ inline qpdf_offset_t
to_offset(T const& i)
{
return IntConverter<T, qpdf_offset_t>::convert(i);
}
template <typename T>
- long
+ inline long
to_long(T const& i)
{
return IntConverter<T, long>::convert(i);
}
template <typename T>
- unsigned long
+ inline unsigned long
to_ulong(T const& i)
{
return IntConverter<T, unsigned long>::convert(i);
}
template <typename T>
- long long
+ inline long long
to_longlong(T const& i)
{
return IntConverter<T, long long>::convert(i);
}
template <typename T>
- unsigned long long
+ inline unsigned long long
to_ulonglong(T const& i)
{
return IntConverter<T, unsigned long long>::convert(i);
@@ -234,12 +258,8 @@ namespace QIntC // QIntC = qpdf Integer Conversion
template <typename T>
void
- range_check(T const& cur, T const& delta)
+ range_check_error(T const& cur, T const& delta)
{
- if ((delta > 0) != (cur > 0)) {
- return;
- }
-
if ((delta > 0) && ((std::numeric_limits<T>::max() - cur) < delta)) {
std::ostringstream msg;
msg.imbue(std::locale::classic());
@@ -257,13 +277,19 @@ namespace QIntC // QIntC = qpdf Integer Conversion
}
template <typename T>
- void
- range_check_substract(T const& cur, T const& delta)
+ inline void
+ range_check(T const& cur, T const& delta)
{
- if ((delta >= 0) == (cur >= 0)) {
+ if ((delta > 0) != (cur > 0)) {
return;
}
+ QIntC::range_check_error<T>(cur, delta);
+ }
+ template <typename T>
+ void
+ range_check_substract_error(T const& cur, T const& delta)
+ {
if ((delta > 0) && ((std::numeric_limits<T>::min() + delta) > cur)) {
std::ostringstream msg;
msg.imbue(std::locale::classic());
@@ -279,6 +305,16 @@ namespace QIntC // QIntC = qpdf Integer Conversion
throw std::range_error(msg.str());
}
}
+
+ template <typename T>
+ inline void
+ range_check_substract(T const& cur, T const& delta)
+ {
+ if ((delta >= 0) == (cur >= 0)) {
+ return;
+ }
+ QIntC::range_check_substract_error<T>(cur, delta);
+ }
}; // namespace QIntC
#endif // QINTC_HH
diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh
index 27521a70..dd452026 100644
--- a/include/qpdf/QUtil.hh
+++ b/include/qpdf/QUtil.hh
@@ -545,13 +545,15 @@ namespace QUtil
inline bool
QUtil::is_hex_digit(char ch)
{
- return (ch && (strchr("0123456789abcdefABCDEF", ch) != nullptr));
+ return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') ||
+ ('A' <= ch && ch <= 'F');
}
inline bool
QUtil::is_space(char ch)
{
- return (ch && (strchr(" \f\n\r\t\v", ch) != nullptr));
+ return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' ||
+ ch == '\v';
}
inline bool
diff --git a/libqpdf/JSON.cc b/libqpdf/JSON.cc
index 1dc09013..76db652b 100644
--- a/libqpdf/JSON.cc
+++ b/libqpdf/JSON.cc
@@ -125,6 +125,7 @@ JSON::JSON_array::write(Pipeline* p, size_t depth) const
}
JSON::JSON_string::JSON_string(std::string const& utf8) :
+ JSON_value(vt_string),
utf8(utf8),
encoded(encode_string(utf8))
{
@@ -137,16 +138,19 @@ JSON::JSON_string::write(Pipeline* p, size_t) const
}
JSON::JSON_number::JSON_number(long long value) :
+ JSON_value(vt_number),
encoded(std::to_string(value))
{
}
JSON::JSON_number::JSON_number(double value) :
+ JSON_value(vt_number),
encoded(QUtil::double_to_string(value, 6))
{
}
JSON::JSON_number::JSON_number(std::string const& value) :
+ JSON_value(vt_number),
encoded(value)
{
}
@@ -158,6 +162,7 @@ JSON::JSON_number::write(Pipeline* p, size_t) const
}
JSON::JSON_bool::JSON_bool(bool val) :
+ JSON_value(vt_bool),
value(val)
{
}
@@ -175,6 +180,7 @@ JSON::JSON_null::write(Pipeline* p, size_t) const
}
JSON::JSON_blob::JSON_blob(std::function<void(Pipeline*)> fn) :
+ JSON_value(vt_blob),
fn(fn)
{
}
@@ -212,41 +218,61 @@ JSON::unparse() const
std::string
JSON::encode_string(std::string const& str)
{
- std::string result;
- size_t len = str.length();
- for (size_t i = 0; i < len; ++i) {
- unsigned char ch = static_cast<unsigned char>(str.at(i));
- switch (ch) {
- case '\\':
- result += "\\\\";
- break;
- case '\"':
- result += "\\\"";
- break;
- case '\b':
- result += "\\b";
- break;
- case '\f':
- result += "\\f";
- break;
- case '\n':
- result += "\\n";
- break;
- case '\r':
- result += "\\r";
- break;
- case '\t':
- result += "\\t";
- break;
- default:
- if (ch < 32) {
- result += "\\u" + QUtil::int_to_string_base(ch, 16, 4);
- } else {
- result.append(1, static_cast<char>(ch));
+ static auto constexpr hexchars = "0123456789abcdef";
+
+ auto begin = str.cbegin();
+ auto end = str.cend();
+ auto iter = begin;
+ while (iter != end) {
+ auto c = static_cast<unsigned char>(*iter);
+ if ((c > 34 && c != '\\') || c == ' ' || c == 33) {
+ // Optimistically check that no char in str requires escaping.
+ // Hopefully we can just return the input str.
+ ++iter;
+ } else {
+ // We found a char that requires escaping. Initialize result to the
+ // chars scanned so far, append/replace the rest of str one char at
+ // a time, and return the result.
+ std::string result{begin, iter};
+
+ for (; iter != end; ++iter) {
+ auto ch = static_cast<unsigned char>(*iter);
+ if ((ch > 34 && ch != '\\') || ch == ' ' || ch == 33) {
+ // Check for most common case first.
+ result += *iter;
+ } else {
+ switch (ch) {
+ case '\\':
+ result += "\\\\";
+ break;
+ case '\"':
+ result += "\\\"";
+ break;
+ case '\b':
+ result += "\\b";
+ break;
+ case '\f':
+ result += "\\f";
+ break;
+ case '\n':
+ result += "\\n";
+ break;
+ case '\r':
+ result += "\\r";
+ break;
+ case '\t':
+ result += "\\t";
+ break;
+ default:
+ result += ch < 16 ? "\\u000" : "\\u001";
+ result += hexchars[ch % 16];
+ }
+ }
}
+ return result;
}
}
- return result;
+ return str;
}
JSON
@@ -348,56 +374,52 @@ JSON::makeBlob(std::function<void(Pipeline*)> fn)
bool
JSON::isArray() const
{
- return nullptr != dynamic_cast<JSON_array const*>(this->m->value.get());
+ return m->value->type_code == vt_array;
}
bool
JSON::isDictionary() const
{
- return nullptr !=
- dynamic_cast<JSON_dictionary const*>(this->m->value.get());
+ return m->value->type_code == vt_dictionary;
}
bool
JSON::getString(std::string& utf8) const
{
- auto v = dynamic_cast<JSON_string const*>(this->m->value.get());
- if (v == nullptr) {
- return false;
+ if (m->value->type_code == vt_string) {
+ auto v = dynamic_cast<JSON_string const*>(this->m->value.get());
+ utf8 = v->utf8;
+ return true;
}
- utf8 = v->utf8;
- return true;
+ return false;
}
bool
JSON::getNumber(std::string& value) const
{
- auto v = dynamic_cast<JSON_number const*>(this->m->value.get());
- if (v == nullptr) {
- return false;
+ if (m->value->type_code == vt_number) {
+ auto v = dynamic_cast<JSON_number const*>(this->m->value.get());
+ value = v->encoded;
+ return true;
}
- value = v->encoded;
- return true;
+ return false;
}
bool
JSON::getBool(bool& value) const
{
- auto v = dynamic_cast<JSON_bool const*>(this->m->value.get());
- if (v == nullptr) {
- return false;
+ if (m->value->type_code == vt_bool) {
+ auto v = dynamic_cast<JSON_bool const*>(this->m->value.get());
+ value = v->value;
+ return true;
}
- value = v->value;
- return true;
+ return false;
}
bool
JSON::isNull() const
{
- if (dynamic_cast<JSON_null const*>(this->m->value.get())) {
- return true;
- }
- return false;
+ return m->value->type_code == vt_null;
}
bool
diff --git a/libqpdf/QPDFTokenizer.cc b/libqpdf/QPDFTokenizer.cc
index fe36d768..990d5b65 100644
--- a/libqpdf/QPDFTokenizer.cc
+++ b/libqpdf/QPDFTokenizer.cc
@@ -14,10 +14,14 @@
#include <stdlib.h>
#include <string.h>
-static bool
+static inline bool
is_delimiter(char ch)
{
- return (strchr(" \t\n\v\f\r()<>[]{}/%", ch) != nullptr);
+ return (
+ ch == ' ' || ch == '\n' || ch == '/' || ch == '(' || ch == ')' ||
+ ch == '{' || ch == '}' || ch == '<' || ch == '>' || ch == '[' ||
+ ch == ']' || ch == '%' || ch == '\t' || ch == '\r' || ch == '\v' ||
+ ch == '\f' || ch == 0);
}
namespace
diff --git a/libqpdf/QPDF_Name.cc b/libqpdf/QPDF_Name.cc
index 1587bcf4..3241ec1c 100644
--- a/libqpdf/QPDF_Name.cc
+++ b/libqpdf/QPDF_Name.cc
@@ -37,7 +37,10 @@ QPDF_Name::normalizeName(std::string const& name)
// QPDFTokenizer embeds a null character to encode an
// invalid #.
result += "#";
- } else if (strchr("#()<>[]{}/%", ch) || (ch < 33) || (ch > 126)) {
+ } else if (
+ ch < 33 || ch == '/' || ch == '(' || ch == ')' || ch == '{' ||
+ ch == '}' || ch == '<' || ch == '>' || ch == '[' || ch == ']' ||
+ ch == '%' || ch > 126) {
result += QUtil::hex_encode_char(ch);
} else {
result += ch;
diff --git a/manual/linearization.rst b/manual/linearization.rst
index 89362bc2..9b0ec50d 100644
--- a/manual/linearization.rst
+++ b/manual/linearization.rst
@@ -4,7 +4,7 @@ Linearization
=============
This chapter describes how ``QPDF`` and ``QPDFWriter`` implement
-creation and processing of linearized PDFS.
+creation and processing of linearized PDFs.
.. _linearization-strategy: