summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BUILD.gn1
-rw-r--r--core/include/fpdfapi/fpdf_parser.h15
-rw-r--r--core/include/fxcrt/fx_system.h1
-rw-r--r--core/src/fpdfapi/fpdf_parser/fpdf_parser_decode_embeddertest.cpp9
-rw-r--r--core/src/fpdfapi/fpdf_parser/fpdf_parser_fdf.cpp2
-rw-r--r--core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp36
-rw-r--r--core/src/fxcrt/fx_basic_gcc.cpp25
-rw-r--r--core/src/fxcrt/fx_basic_gcc_unittest.cpp88
-rw-r--r--pdfium.gyp1
-rw-r--r--testing/resources/bug_455199.pdf73
10 files changed, 209 insertions, 42 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 3f99c28f0a..7e7f4dd519 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1435,6 +1435,7 @@ test("pdfium_unittests") {
"core/src/fpdftext/fpdf_text_int_unittest.cpp",
"core/src/fxcodec/codec/fx_codec_jpx_unittest.cpp",
"core/src/fxcrt/fx_basic_bstring_unittest.cpp",
+ "core/src/fxcrt/fx_basic_gcc_unittest.cpp",
"core/src/fxcrt/fx_basic_memmgr_unittest.cpp",
"core/src/fxcrt/fx_basic_wstring_unittest.cpp",
"core/src/fxcrt/fx_bidi_unittest.cpp",
diff --git a/core/include/fpdfapi/fpdf_parser.h b/core/include/fpdfapi/fpdf_parser.h
index c57d9f20b9..5903e82a16 100644
--- a/core/include/fpdfapi/fpdf_parser.h
+++ b/core/include/fpdfapi/fpdf_parser.h
@@ -239,43 +239,33 @@ class CPDF_SyntaxParser {
void InitParser(IFX_FileRead* pFileAccess, FX_DWORD HeaderOffset);
FX_FILESIZE SavePos() const { return m_Pos; }
-
void RestorePos(FX_FILESIZE pos) { m_Pos = pos; }
CPDF_Object* GetObject(CPDF_IndirectObjectHolder* pObjList,
FX_DWORD objnum,
FX_DWORD gennum,
FX_BOOL bDecrypt);
-
CPDF_Object* GetObjectByStrict(CPDF_IndirectObjectHolder* pObjList,
FX_DWORD objnum,
FX_DWORD gennum);
-
- int GetDirectNum();
-
CFX_ByteString GetKeyword();
void ToNextLine();
-
void ToNextWord();
FX_BOOL SearchWord(const CFX_ByteStringC& word,
FX_BOOL bWholeWord,
FX_BOOL bForward,
FX_FILESIZE limit);
-
int SearchMultiWord(const CFX_ByteStringC& words,
FX_BOOL bWholeWord,
FX_FILESIZE limit);
-
FX_FILESIZE FindTag(const CFX_ByteStringC& tag, FX_FILESIZE limit);
void SetEncrypt(std::unique_ptr<CPDF_CryptoHandler> pCryptoHandler);
- FX_BOOL GetCharAt(FX_FILESIZE pos, uint8_t& ch);
-
FX_BOOL ReadBlock(uint8_t* pBuf, FX_DWORD size);
-
+ FX_BOOL GetCharAt(FX_FILESIZE pos, uint8_t& ch);
CFX_ByteString GetNextWord(bool* bIsNumber);
protected:
@@ -318,6 +308,9 @@ class CPDF_SyntaxParser {
std::unique_ptr<CPDF_CryptoHandler> m_pCryptoHandler;
uint8_t m_WordBuffer[257];
FX_DWORD m_WordSize;
+
+ private:
+ uint32_t GetDirectNum();
};
class CPDF_Parser {
diff --git a/core/include/fxcrt/fx_system.h b/core/include/fxcrt/fx_system.h
index e28099ed4a..462fb3babf 100644
--- a/core/include/fxcrt/fx_system.h
+++ b/core/include/fxcrt/fx_system.h
@@ -264,6 +264,7 @@ wchar_t* FXSYS_wcsupr(wchar_t* str);
#define FXSYS_HIWORD(dword) ((FX_WORD)((dword) >> 16))
#define FXSYS_LOWORD(dword) ((FX_WORD)(dword))
int32_t FXSYS_atoi(const FX_CHAR* str);
+uint32_t FXSYS_atoui(const FX_CHAR* str);
int32_t FXSYS_wtoi(const FX_WCHAR* str);
int64_t FXSYS_atoi64(const FX_CHAR* str);
int64_t FXSYS_wtoi64(const FX_WCHAR* str);
diff --git a/core/src/fpdfapi/fpdf_parser/fpdf_parser_decode_embeddertest.cpp b/core/src/fpdfapi/fpdf_parser/fpdf_parser_decode_embeddertest.cpp
index 9bb9d41db1..7ed82fdfb6 100644
--- a/core/src/fpdfapi/fpdf_parser/fpdf_parser_decode_embeddertest.cpp
+++ b/core/src/fpdfapi/fpdf_parser/fpdf_parser_decode_embeddertest.cpp
@@ -105,3 +105,12 @@ TEST_F(FPDFParserDecodeEmbeddertest, Bug_555784) {
UnloadPage(page);
}
+TEST_F(FPDFParserDecodeEmbeddertest, Bug_455199) {
+ // Tests object numbers with a value > 01000000.
+ // Should open successfully.
+ EXPECT_TRUE(OpenDocument("bug_455199.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ FPDF_BITMAP bitmap = RenderPage(page);
+ FPDFBitmap_Destroy(bitmap);
+ UnloadPage(page);
+}
diff --git a/core/src/fpdfapi/fpdf_parser/fpdf_parser_fdf.cpp b/core/src/fpdfapi/fpdf_parser/fpdf_parser_fdf.cpp
index b80568b63c..fbe6a8fb3a 100644
--- a/core/src/fpdfapi/fpdf_parser/fpdf_parser_fdf.cpp
+++ b/core/src/fpdfapi/fpdf_parser/fpdf_parser_fdf.cpp
@@ -51,7 +51,7 @@ void CFDF_Document::ParseStream(IFX_FileRead* pFile, FX_BOOL bOwnFile) {
bool bNumber;
CFX_ByteString word = parser.GetNextWord(&bNumber);
if (bNumber) {
- FX_DWORD objnum = FXSYS_atoi(word);
+ FX_DWORD objnum = FXSYS_atoui(word);
word = parser.GetNextWord(&bNumber);
if (!bNumber) {
break;
diff --git a/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp b/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp
index 825a8a26d4..16d1134cb1 100644
--- a/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp
+++ b/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp
@@ -534,7 +534,7 @@ bool CPDF_Parser::LoadCrossRefV4(FX_FILESIZE pos,
break;
}
- FX_DWORD start_objnum = FXSYS_atoi(word);
+ FX_DWORD start_objnum = FXSYS_atoui(word);
if (start_objnum >= kMaxObjectNumber)
return false;
@@ -758,10 +758,6 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
break;
case 3:
if (PDFCharIsWhitespace(byte) || PDFCharIsDelimiter(byte)) {
- if (objnum > 0x1000000) {
- state = ParserState::kDefault;
- break;
- }
FX_FILESIZE obj_pos = start_pos - m_Syntax.m_HeaderOffset;
m_SortedOffset.insert(obj_pos);
last_obj = start_pos;
@@ -1323,7 +1319,7 @@ void CPDF_Parser::GetIndirectBinary(FX_DWORD objnum,
return;
}
- FX_DWORD parser_objnum = FXSYS_atoi(word);
+ FX_DWORD parser_objnum = FXSYS_atoui(word);
if (parser_objnum && parser_objnum != objnum) {
m_Syntax.RestorePos(SavedPos);
return;
@@ -1395,7 +1391,7 @@ CPDF_Object* CPDF_Parser::ParseIndirectObjectAt(
FX_FILESIZE objOffset = m_Syntax.SavePos();
objOffset -= word.GetLength();
- FX_DWORD parser_objnum = FXSYS_atoi(word);
+ FX_DWORD parser_objnum = FXSYS_atoui(word);
if (objnum && parser_objnum != objnum) {
m_Syntax.RestorePos(SavedPos);
return nullptr;
@@ -1407,7 +1403,7 @@ CPDF_Object* CPDF_Parser::ParseIndirectObjectAt(
return nullptr;
}
- FX_DWORD parser_gennum = FXSYS_atoi(word);
+ FX_DWORD parser_gennum = FXSYS_atoui(word);
if (m_Syntax.GetKeyword() != "obj") {
m_Syntax.RestorePos(SavedPos);
return nullptr;
@@ -1444,7 +1440,7 @@ CPDF_Object* CPDF_Parser::ParseIndirectObjectAtByStrict(
return nullptr;
}
- FX_DWORD parser_objnum = FXSYS_atoi(word);
+ FX_DWORD parser_objnum = FXSYS_atoui(word);
if (objnum && parser_objnum != objnum) {
m_Syntax.RestorePos(SavedPos);
return nullptr;
@@ -1456,7 +1452,7 @@ CPDF_Object* CPDF_Parser::ParseIndirectObjectAtByStrict(
return nullptr;
}
- FX_DWORD gennum = FXSYS_atoi(word);
+ FX_DWORD gennum = FXSYS_atoui(word);
if (m_Syntax.GetKeyword() != "obj") {
m_Syntax.RestorePos(SavedPos);
return nullptr;
@@ -1506,12 +1502,12 @@ FX_BOOL CPDF_Parser::IsLinearizedFile(IFX_FileRead* pFileAccess,
if (!bIsNumber)
return FALSE;
- FX_DWORD objnum = FXSYS_atoi(word);
+ FX_DWORD objnum = FXSYS_atoui(word);
word = m_Syntax.GetNextWord(&bIsNumber);
if (!bIsNumber)
return FALSE;
- FX_DWORD gennum = FXSYS_atoi(word);
+ FX_DWORD gennum = FXSYS_atoui(word);
if (m_Syntax.GetKeyword() != "obj") {
m_Syntax.RestorePos(SavedPos);
return FALSE;
@@ -2039,7 +2035,7 @@ CPDF_Object* CPDF_SyntaxParser::GetObject(CPDF_IndirectObjectHolder* pObjList,
if (bIsNumber) {
CFX_ByteString nextword2 = GetNextWord(nullptr);
if (nextword2 == "R") {
- FX_DWORD objnum = FXSYS_atoi(word);
+ FX_DWORD objnum = FXSYS_atoui(word);
return new CPDF_Reference(pObjList, objnum);
}
}
@@ -2163,7 +2159,7 @@ CPDF_Object* CPDF_SyntaxParser::GetObjectByStrict(
if (bIsNumber) {
CFX_ByteString nextword2 = GetNextWord(nullptr);
if (nextword2 == "R")
- return new CPDF_Reference(pObjList, FXSYS_atoi(word));
+ return new CPDF_Reference(pObjList, FXSYS_atoui(word));
}
m_Pos = SavedPos;
return new CPDF_Number(word);
@@ -2440,14 +2436,14 @@ void CPDF_SyntaxParser::InitParser(IFX_FileRead* pFileAccess,
(size_t)((FX_FILESIZE)m_BufSize > m_FileLen ? m_FileLen : m_BufSize));
}
-int32_t CPDF_SyntaxParser::GetDirectNum() {
+uint32_t CPDF_SyntaxParser::GetDirectNum() {
bool bIsNumber;
GetNextWordInternal(&bIsNumber);
if (!bIsNumber)
return 0;
m_WordBuffer[m_WordSize] = 0;
- return FXSYS_atoi(reinterpret_cast<const FX_CHAR*>(m_WordBuffer));
+ return FXSYS_atoui(reinterpret_cast<const FX_CHAR*>(m_WordBuffer));
}
bool CPDF_SyntaxParser::IsWholeWord(FX_FILESIZE startpos,
@@ -3554,7 +3550,7 @@ CPDF_Object* CPDF_DataAvail::ParseIndirectObjectAt(
if (!bIsNumber)
return nullptr;
- FX_DWORD parser_objnum = FXSYS_atoi(word);
+ FX_DWORD parser_objnum = FXSYS_atoui(word);
if (objnum && parser_objnum != objnum)
return nullptr;
@@ -3562,7 +3558,7 @@ CPDF_Object* CPDF_DataAvail::ParseIndirectObjectAt(
if (!bIsNumber)
return nullptr;
- FX_DWORD gennum = FXSYS_atoi(word);
+ FX_DWORD gennum = FXSYS_atoui(word);
if (m_syntaxParser.GetKeyword() != "obj") {
m_syntaxParser.RestorePos(SavedPos);
return nullptr;
@@ -3611,7 +3607,7 @@ FX_BOOL CPDF_DataAvail::IsLinearizedFile(uint8_t* pData, FX_DWORD dwLen) {
if (!bNumber)
return FALSE;
- FX_DWORD objnum = FXSYS_atoi(wordObjNum);
+ FX_DWORD objnum = FXSYS_atoui(wordObjNum);
if (m_pLinearized) {
m_pLinearized->Release();
m_pLinearized = nullptr;
@@ -3704,7 +3700,7 @@ int32_t CPDF_DataAvail::CheckCrossRefStream(IFX_DownloadHints* pHints,
if (!bNumber)
return -1;
- FX_DWORD objNum = FXSYS_atoi(objnum);
+ FX_DWORD objNum = FXSYS_atoui(objnum);
CPDF_Object* pObj = m_parser.ParseIndirectObjectAt(nullptr, 0, objNum);
if (!pObj) {
m_Pos += m_parser.m_Syntax.SavePos();
diff --git a/core/src/fxcrt/fx_basic_gcc.cpp b/core/src/fxcrt/fx_basic_gcc.cpp
index d905d6b498..607f095570 100644
--- a/core/src/fxcrt/fx_basic_gcc.cpp
+++ b/core/src/fxcrt/fx_basic_gcc.cpp
@@ -13,20 +13,21 @@
template <class T>
T FXSYS_StrToInt(const FX_CHAR* str) {
- FX_BOOL neg = FALSE;
+ bool neg = false;
if (!str)
return 0;
- if (*str == '-') {
- neg = TRUE;
+ if (std::numeric_limits<T>::is_signed && *str == '-') {
+ neg = true;
str++;
}
T num = 0;
while (*str && std::isdigit(*str)) {
- if (num > (std::numeric_limits<T>::max() - 9) / 10)
+ T val = FXSYS_toDecimalDigit(*str);
+ if (num > (std::numeric_limits<T>::max() - val) / 10)
break;
- num = num * 10 + FXSYS_toDecimalDigit(*str);
+ num = num * 10 + val;
str++;
}
return neg ? -num : num;
@@ -34,20 +35,21 @@ T FXSYS_StrToInt(const FX_CHAR* str) {
template <class T>
T FXSYS_StrToInt(const FX_WCHAR* str) {
- FX_BOOL neg = FALSE;
+ bool neg = false;
if (!str)
return 0;
- if (*str == '-') {
- neg = TRUE;
+ if (std::numeric_limits<T>::is_signed && *str == '-') {
+ neg = true;
str++;
}
T num = 0;
while (*str && std::iswdigit(*str)) {
- if (num > (std::numeric_limits<T>::max() - 9) / 10)
+ T val = FXSYS_toDecimalDigitWide(*str);
+ if (num > (std::numeric_limits<T>::max() - val) / 10)
break;
- num = num * 10 + FXSYS_toDecimalDigitWide(*str);
+ num = num * 10 + val;
str++;
}
return neg ? -num : num;
@@ -93,6 +95,9 @@ extern "C" {
int32_t FXSYS_atoi(const FX_CHAR* str) {
return FXSYS_StrToInt<int32_t>(str);
}
+uint32_t FXSYS_atoui(const FX_CHAR* str) {
+ return FXSYS_StrToInt<uint32_t>(str);
+}
int32_t FXSYS_wtoi(const FX_WCHAR* str) {
return FXSYS_StrToInt<int32_t>(str);
}
diff --git a/core/src/fxcrt/fx_basic_gcc_unittest.cpp b/core/src/fxcrt/fx_basic_gcc_unittest.cpp
new file mode 100644
index 0000000000..eb1e0669ae
--- /dev/null
+++ b/core/src/fxcrt/fx_basic_gcc_unittest.cpp
@@ -0,0 +1,88 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/include/fxcrt/fx_system.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcrt, FXSYS_atoi) {
+ EXPECT_EQ(0, FXSYS_atoi(""));
+ EXPECT_EQ(0, FXSYS_atoi("0"));
+ EXPECT_EQ(-1, FXSYS_atoi("-1"));
+ EXPECT_EQ(2345, FXSYS_atoi("2345"));
+ EXPECT_EQ(2147483647, FXSYS_atoi("2147483647"));
+ EXPECT_EQ(-2147483647, FXSYS_atoi("-2147483647"));
+ EXPECT_EQ(9, FXSYS_atoi("9x9"));
+
+ // TODO(dsinclair): These are all wacky .....
+ EXPECT_EQ(2147483623, FXSYS_atoi("2147483623423412348"));
+ EXPECT_EQ(214748364, FXSYS_atoi("2147483648"));
+ // The digit is parsed as a positive value, so we end up not being able to
+ // handle the largest possible negative value.
+ EXPECT_EQ(-214748364, FXSYS_atoi("-2147483648"));
+}
+
+TEST(fxcrt, FXSYS_atoi64) {
+ EXPECT_EQ(0, FXSYS_atoi64(""));
+ EXPECT_EQ(0, FXSYS_atoi64("0"));
+ EXPECT_EQ(-1, FXSYS_atoi64("-1"));
+ EXPECT_EQ(2345, FXSYS_atoi64("2345"));
+ EXPECT_EQ(9223372036854775807LL, FXSYS_atoi64("9223372036854775807"));
+ EXPECT_EQ(-9223372036854775807LL, FXSYS_atoi64("-9223372036854775807"));
+ EXPECT_EQ(9, FXSYS_atoi64("9x9"));
+
+ // TODO(dsinclair): These are all wacky .....
+ EXPECT_EQ(9223372036854712341LL, FXSYS_atoi64("922337203685471234123475807"));
+ EXPECT_EQ(922337203685477580LL, FXSYS_atoi64("9223372036854775808"));
+ // The digit is parsed as a positive value, so we end up not being able to
+ // handle the largest possible negative value.
+ EXPECT_EQ(-922337203685477580LL, FXSYS_atoi64("-9223372036854775808"));
+}
+
+TEST(fxcrt, FXSYS_wtoi) {
+ EXPECT_EQ(0, FXSYS_wtoi(L""));
+ EXPECT_EQ(0, FXSYS_wtoi(L"0"));
+ EXPECT_EQ(-1, FXSYS_wtoi(L"-1"));
+ EXPECT_EQ(2345, FXSYS_wtoi(L"2345"));
+ EXPECT_EQ(2147483647, FXSYS_wtoi(L"2147483647"));
+ EXPECT_EQ(-2147483647, FXSYS_wtoi(L"-2147483647"));
+ EXPECT_EQ(9, FXSYS_wtoi64(L"9x9"));
+
+ // TODO(dsinclair): These are all wacky .....
+ EXPECT_EQ(2147483623, FXSYS_wtoi(L"2147483623423412348"));
+ EXPECT_EQ(214748364, FXSYS_wtoi(L"2147483648"));
+ // The digit is parsed as a positive value, so we end up not being able to
+ // handle the largest possible negative value.
+ EXPECT_EQ(-214748364, FXSYS_wtoi(L"-2147483648"));
+}
+
+TEST(fxcrt, FXSYS_wtoi64) {
+ EXPECT_EQ(0, FXSYS_wtoi64(L""));
+ EXPECT_EQ(0, FXSYS_wtoi64(L"0"));
+ EXPECT_EQ(-1, FXSYS_wtoi64(L"-1"));
+ EXPECT_EQ(2345, FXSYS_wtoi64(L"2345"));
+ EXPECT_EQ(9223372036854775807LL, FXSYS_wtoi64(L"9223372036854775807"));
+ EXPECT_EQ(-9223372036854775807LL, FXSYS_wtoi64(L"-9223372036854775807"));
+ EXPECT_EQ(9, FXSYS_wtoi64(L"9x9"));
+
+ // TODO(dsinclair): These are all wacky .....
+ EXPECT_EQ(9223372036854712341LL,
+ FXSYS_wtoi64(L"922337203685471234123475807"));
+ EXPECT_EQ(922337203685477580LL, FXSYS_wtoi64(L"9223372036854775808"));
+ // The digit is parsed as a positive value, so we end up not being able to
+ // handle the largest possible negative value.
+ EXPECT_EQ(-922337203685477580LL, FXSYS_wtoi64(L"-9223372036854775808"));
+}
+
+TEST(fxcrt, FXSYS_atoui) {
+ EXPECT_EQ(0, FXSYS_atoui(""));
+ EXPECT_EQ(0, FXSYS_atoui("0"));
+ EXPECT_EQ(0, FXSYS_atoui("-1"));
+ EXPECT_EQ(2345, FXSYS_atoui("2345"));
+ EXPECT_EQ(4294967295, FXSYS_atoui("4294967295"));
+ EXPECT_EQ(9, FXSYS_atoui("9x9"));
+
+ // TODO(dsinclair): These are all wacky .....
+ EXPECT_EQ(2147483623, FXSYS_atoi("2147483623423412348"));
+ EXPECT_EQ(429496729, FXSYS_atoi("4294967296"));
+}
diff --git a/pdfium.gyp b/pdfium.gyp
index 14d18e1499..08d3f085f0 100644
--- a/pdfium.gyp
+++ b/pdfium.gyp
@@ -757,6 +757,7 @@
'core/src/fpdftext/fpdf_text_int_unittest.cpp',
'core/src/fxcodec/codec/fx_codec_jpx_unittest.cpp',
'core/src/fxcrt/fx_basic_bstring_unittest.cpp',
+ 'core/src/fxcrt/fx_basic_gcc_unittest.cpp',
'core/src/fxcrt/fx_basic_memmgr_unittest.cpp',
'core/src/fxcrt/fx_basic_wstring_unittest.cpp',
'core/src/fxcrt/fx_bidi_unittest.cpp',
diff --git a/testing/resources/bug_455199.pdf b/testing/resources/bug_455199.pdf
new file mode 100644
index 0000000000..466affa4d1
--- /dev/null
+++ b/testing/resources/bug_455199.pdf
@@ -0,0 +1,73 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [ 0 0 200 200 ]
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ /F2 5 0 R
+ >>
+ >>
+ /Contents [6 0 R 7 0 R]
+>>
+endobj
+4 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+2147483648 0 obj
+<</Length 163/Filter/FlateDecode>>stream
+x<9c><85><8e>±
+Â0^@^E÷|Å<9b>DAc<82>iSÝ,ZpP(fpU^ZKÔ64|Füzãè$·^^Ü¡&C¢T<84><83>%8¸^T<94>|_äBÒ,<83>êÈüd<84>^@^WPׯD3Æ<97>yR]KÆ^[^[û<87>=7ºAål·B<8c><91>^¼|_ôý^Zh¯ÃDÝ^HCK¶<8a>Ô¿^]Yм<80>d<94>-þU*ë°·N£<Îv
+¥µw?ÅÁ^Fí1ÂÚ{Óö<9d>î<83>ÇÓ¤ö<9f><8b><9a>|^@ 9@Ø
+endstream
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000061 00000 n
+0000000154 00000 n
+0000000305 00000 n
+0000000695 00000 n
+0000000771 00000 n
+trailer <<
+ /Size 6
+ /Root 1 0 R
+>>
+startxref
+892
+%%EOF