2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 10:58:58 +00:00
qpdf/libqpdf/QPDF_String.cc
2022-02-22 08:40:49 -05:00

200 lines
4.6 KiB
C++

#include <qpdf/QPDF_String.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/QTC.hh>
// DO NOT USE ctype -- it is locale dependent for some things, and
// it's not worth the risk of including it in case it may accidentally
// be used.
#include <string.h>
// See above about ctype.
static bool is_ascii_printable(char ch)
{
return ((ch >= 32) && (ch <= 126));
}
static bool is_iso_latin1_printable(char ch)
{
return (((ch >= 32) && (ch <= 126)) ||
(static_cast<unsigned char>(ch) >= 160));
}
QPDF_String::QPDF_String(std::string const& val) :
val(val)
{
}
QPDF_String::~QPDF_String()
{
}
QPDF_String*
QPDF_String::new_utf16(std::string const& utf8_val)
{
std::string result;
if (! QUtil::utf8_to_pdf_doc(utf8_val, result, '?'))
{
result = QUtil::utf8_to_utf16(utf8_val);
}
return new QPDF_String(result);
}
std::string
QPDF_String::unparse()
{
return unparse(false);
}
JSON
QPDF_String::getJSON()
{
return JSON::makeString(getUTF8Val());
}
QPDFObject::object_type_e
QPDF_String::getTypeCode() const
{
return QPDFObject::ot_string;
}
char const*
QPDF_String::getTypeName() const
{
return "string";
}
std::string
QPDF_String::unparse(bool force_binary)
{
bool use_hexstring = force_binary;
if (! use_hexstring)
{
unsigned int nonprintable = 0;
int consecutive_printable = 0;
for (unsigned int i = 0; i < this->val.length(); ++i)
{
char ch = this->val.at(i);
// Note: do not use locale to determine printability. The
// PDF specification accepts arbitrary binary data. Some
// locales imply multibyte characters. We'll consider
// something printable if it is printable in 7-bit ASCII.
// We'll code this manually rather than being rude and
// setting locale.
if ((ch == 0) || (! (is_ascii_printable(ch) ||
strchr("\n\r\t\b\f", ch))))
{
++nonprintable;
consecutive_printable = 0;
}
else
{
if (++consecutive_printable > 5)
{
// If there are more than 5 consecutive printable
// characters, I want to see them as such.
nonprintable = 0;
break;
}
}
}
// Use hex notation if more than 20% of the characters are not
// printable in plain ASCII.
if (5 * nonprintable > val.length())
{
use_hexstring = true;
}
}
std::string result;
if (use_hexstring)
{
result += "<" + QUtil::hex_encode(this->val) + ">";
}
else
{
result += "(";
for (unsigned int i = 0; i < this->val.length(); ++i)
{
char ch = this->val.at(i);
switch (ch)
{
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '(':
result += "\\(";
break;
case ')':
result += "\\)";
break;
case '\\':
result += "\\\\";
break;
default:
if (is_iso_latin1_printable(ch))
{
result += this->val.at(i);
}
else
{
result += "\\" + QUtil::int_to_string_base(
static_cast<int>(static_cast<unsigned char>(ch)),
8, 3);
}
break;
}
}
result += ")";
}
return result;
}
std::string
QPDF_String::getVal() const
{
return this->val;
}
std::string
QPDF_String::getUTF8Val() const
{
if (QUtil::is_utf16(this->val))
{
return QUtil::utf16_to_utf8(this->val);
}
else if ((val.length() >= 3) &&
(val.at(0) == '\xEF') &&
(val.at(1) == '\xBB') &&
(val.at(2) == '\xBF'))
{
// PDF 2.0 allows UTF-8 strings when explicitly prefixed with
// the above bytes, which is just UTF-8 encoding of U+FEFF.
return this->val.substr(3);
}
else
{
return QUtil::pdf_doc_to_utf8(this->val);
}
}