From 6eb7939300d1bc7c31afd5086c1b93d4a7628481 Mon Sep 17 00:00:00 2001 From: Henrique Nakashima Date: Tue, 12 Jun 2018 20:27:35 +0000 Subject: Fill m_ContentStream field in CPDF_PageObject. From the comment of CPDF_PageObject::GetContentStream(): """ Get what content stream the object was parsed from in its page. This number is the index of the content stream in the "Contents" array, or 0 if there is a single content stream. If the object is newly created, -1 is returned. If the object is spread among more than one content stream, this is the index of the last one. """ Bug: pdfium:1051 Change-Id: I9f7804af4f263dda0422e9542e025e3320ff7c31 Reviewed-on: https://pdfium-review.googlesource.com/34250 Commit-Queue: Henrique Nakashima Reviewed-by: dsinclair --- core/fpdfapi/page/cpdf_contentparser.cpp | 15 ++- core/fpdfapi/page/cpdf_contentparser.h | 1 + core/fpdfapi/page/cpdf_formobject.cpp | 7 +- core/fpdfapi/page/cpdf_formobject.h | 4 +- core/fpdfapi/page/cpdf_imageobject.cpp | 5 +- core/fpdfapi/page/cpdf_imageobject.h | 1 + core/fpdfapi/page/cpdf_pageobject.cpp | 7 +- core/fpdfapi/page/cpdf_pageobject.h | 12 +++ core/fpdfapi/page/cpdf_pathobject.cpp | 5 +- core/fpdfapi/page/cpdf_pathobject.h | 1 + core/fpdfapi/page/cpdf_shadingobject.cpp | 5 +- core/fpdfapi/page/cpdf_shadingobject.h | 4 +- core/fpdfapi/page/cpdf_streamcontentparser.cpp | 41 ++++--- core/fpdfapi/page/cpdf_streamcontentparser.h | 7 +- core/fpdfapi/page/cpdf_textobject.cpp | 5 +- core/fpdfapi/page/cpdf_textobject.h | 1 + fpdfsdk/fpdf_edit_embeddertest.cpp | 29 +++++ testing/resources/split_streams.in | 124 +++++++++++++++++++++ testing/resources/split_streams.pdf | 142 +++++++++++++++++++++++++ 19 files changed, 388 insertions(+), 28 deletions(-) create mode 100644 testing/resources/split_streams.in create mode 100644 testing/resources/split_streams.pdf diff --git a/core/fpdfapi/page/cpdf_contentparser.cpp b/core/fpdfapi/page/cpdf_contentparser.cpp index 77cfade1da..2bb376e841 100644 --- a/core/fpdfapi/page/cpdf_contentparser.cpp +++ b/core/fpdfapi/page/cpdf_contentparser.cpp @@ -164,11 +164,13 @@ CPDF_ContentParser::Stage CPDF_ContentParser::PrepareContent() { FX_SAFE_UINT32 safeSize = 0; for (const auto& stream : m_StreamArray) { + m_StreamSegmentOffsets.push_back(safeSize.ValueOrDie()); + safeSize += stream->GetSize(); safeSize += 1; + if (!safeSize.IsValid()) + return Stage::kComplete; } - if (!safeSize.IsValid()) - return Stage::kComplete; m_Size = safeSize.ValueOrDie(); m_pData.Reset( @@ -198,9 +200,12 @@ CPDF_ContentParser::Stage CPDF_ContentParser::Parse() { if (m_CurrentOffset >= m_Size) return Stage::kCheckClip; - m_CurrentOffset += - m_pParser->Parse(m_pData.Get() + m_CurrentOffset, - m_Size - m_CurrentOffset, PARSE_STEP_LIMIT); + if (m_StreamSegmentOffsets.empty()) + m_StreamSegmentOffsets.push_back(0); + + m_CurrentOffset += m_pParser->Parse(m_pData.Get() + m_CurrentOffset, + m_Size - m_CurrentOffset, + PARSE_STEP_LIMIT, m_StreamSegmentOffsets); return Stage::kParse; } diff --git a/core/fpdfapi/page/cpdf_contentparser.h b/core/fpdfapi/page/cpdf_contentparser.h index f9b491defa..b5db5d5e13 100644 --- a/core/fpdfapi/page/cpdf_contentparser.h +++ b/core/fpdfapi/page/cpdf_contentparser.h @@ -58,6 +58,7 @@ class CPDF_ContentParser { UnownedPtr m_pType3Char; // Only used when parsing forms. RetainPtr m_pSingleStream; std::vector> m_StreamArray; + std::vector m_StreamSegmentOffsets; MaybeOwned m_pData; uint32_t m_nStreams = 0; uint32_t m_Size = 0; diff --git a/core/fpdfapi/page/cpdf_formobject.cpp b/core/fpdfapi/page/cpdf_formobject.cpp index eca92ca0ff..22ac0d3823 100644 --- a/core/fpdfapi/page/cpdf_formobject.cpp +++ b/core/fpdfapi/page/cpdf_formobject.cpp @@ -10,9 +10,12 @@ #include "core/fpdfapi/page/cpdf_form.h" -CPDF_FormObject::CPDF_FormObject(std::unique_ptr pForm, +CPDF_FormObject::CPDF_FormObject(int32_t content_stream, + std::unique_ptr pForm, const CFX_Matrix& matrix) - : m_pForm(std::move(pForm)), m_FormMatrix(matrix) {} + : CPDF_PageObject(content_stream), + m_pForm(std::move(pForm)), + m_FormMatrix(matrix) {} CPDF_FormObject::~CPDF_FormObject() {} diff --git a/core/fpdfapi/page/cpdf_formobject.h b/core/fpdfapi/page/cpdf_formobject.h index c723cc0eaf..b229dce444 100644 --- a/core/fpdfapi/page/cpdf_formobject.h +++ b/core/fpdfapi/page/cpdf_formobject.h @@ -16,7 +16,9 @@ class CPDF_Form; class CPDF_FormObject : public CPDF_PageObject { public: - CPDF_FormObject(std::unique_ptr pForm, const CFX_Matrix& matrix); + CPDF_FormObject(int32_t content_stream, + std::unique_ptr pForm, + const CFX_Matrix& matrix); ~CPDF_FormObject() override; // CPDF_PageObject: diff --git a/core/fpdfapi/page/cpdf_imageobject.cpp b/core/fpdfapi/page/cpdf_imageobject.cpp index 3b5a740155..516a6e8cad 100644 --- a/core/fpdfapi/page/cpdf_imageobject.cpp +++ b/core/fpdfapi/page/cpdf_imageobject.cpp @@ -12,7 +12,10 @@ #include "core/fpdfapi/page/cpdf_image.h" #include "core/fpdfapi/parser/cpdf_document.h" -CPDF_ImageObject::CPDF_ImageObject() {} +CPDF_ImageObject::CPDF_ImageObject(int32_t content_stream) + : CPDF_PageObject(content_stream) {} + +CPDF_ImageObject::CPDF_ImageObject() : CPDF_ImageObject(kNoContentStream) {} CPDF_ImageObject::~CPDF_ImageObject() { MaybePurgeCache(); diff --git a/core/fpdfapi/page/cpdf_imageobject.h b/core/fpdfapi/page/cpdf_imageobject.h index 16a506e537..d54ef8d818 100644 --- a/core/fpdfapi/page/cpdf_imageobject.h +++ b/core/fpdfapi/page/cpdf_imageobject.h @@ -16,6 +16,7 @@ class CPDF_Image; class CPDF_ImageObject : public CPDF_PageObject { public: + explicit CPDF_ImageObject(int32_t content_stream); CPDF_ImageObject(); ~CPDF_ImageObject() override; diff --git a/core/fpdfapi/page/cpdf_pageobject.cpp b/core/fpdfapi/page/cpdf_pageobject.cpp index 8bb5bf5978..604309f6e6 100644 --- a/core/fpdfapi/page/cpdf_pageobject.cpp +++ b/core/fpdfapi/page/cpdf_pageobject.cpp @@ -6,7 +6,12 @@ #include "core/fpdfapi/page/cpdf_pageobject.h" -CPDF_PageObject::CPDF_PageObject() : m_bDirty(false) {} +constexpr int32_t CPDF_PageObject::kNoContentStream; + +CPDF_PageObject::CPDF_PageObject(int32_t content_stream) + : m_bDirty(false), m_ContentStream(content_stream) {} + +CPDF_PageObject::CPDF_PageObject() : CPDF_PageObject(kNoContentStream) {} CPDF_PageObject::~CPDF_PageObject() {} diff --git a/core/fpdfapi/page/cpdf_pageobject.h b/core/fpdfapi/page/cpdf_pageobject.h index d23cd971f8..39e7629541 100644 --- a/core/fpdfapi/page/cpdf_pageobject.h +++ b/core/fpdfapi/page/cpdf_pageobject.h @@ -28,6 +28,9 @@ class CPDF_PageObject : public CPDF_GraphicStates { FORM, }; + static constexpr int32_t kNoContentStream = -1; + + explicit CPDF_PageObject(int32_t content_stream); CPDF_PageObject(); ~CPDF_PageObject() override; @@ -59,6 +62,14 @@ class CPDF_PageObject : public CPDF_GraphicStates { } FX_RECT GetBBox(const CFX_Matrix* pMatrix) const; + // Get what content stream the object was parsed from in its page. This number + // is the index of the content stream in the "Contents" array, or 0 if there + // is a single content stream. If the object is newly created, + // kNoContentStream is returned. + // If the object is spread among more than one content stream, this is the + // index of the last stream. + int32_t GetContentStream() const { return m_ContentStream; } + float m_Left; float m_Right; float m_Top; @@ -73,6 +84,7 @@ class CPDF_PageObject : public CPDF_GraphicStates { void operator=(const CPDF_PageObject& src) = delete; bool m_bDirty; + int32_t m_ContentStream; }; #endif // CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_ diff --git a/core/fpdfapi/page/cpdf_pathobject.cpp b/core/fpdfapi/page/cpdf_pathobject.cpp index d8c2cb8741..0882dc17f0 100644 --- a/core/fpdfapi/page/cpdf_pathobject.cpp +++ b/core/fpdfapi/page/cpdf_pathobject.cpp @@ -6,7 +6,10 @@ #include "core/fpdfapi/page/cpdf_pathobject.h" -CPDF_PathObject::CPDF_PathObject() : m_FillType(0), m_bStroke(false) {} +CPDF_PathObject::CPDF_PathObject(int32_t content_stream) + : CPDF_PageObject(content_stream), m_FillType(0), m_bStroke(false) {} + +CPDF_PathObject::CPDF_PathObject() : CPDF_PathObject(kNoContentStream) {} CPDF_PathObject::~CPDF_PathObject() {} diff --git a/core/fpdfapi/page/cpdf_pathobject.h b/core/fpdfapi/page/cpdf_pathobject.h index 58499b5a53..5155c40c3d 100644 --- a/core/fpdfapi/page/cpdf_pathobject.h +++ b/core/fpdfapi/page/cpdf_pathobject.h @@ -14,6 +14,7 @@ class CPDF_PathObject : public CPDF_PageObject { public: + explicit CPDF_PathObject(int32_t content_stream); CPDF_PathObject(); ~CPDF_PathObject() override; diff --git a/core/fpdfapi/page/cpdf_shadingobject.cpp b/core/fpdfapi/page/cpdf_shadingobject.cpp index 1b16ac4862..725e2e4591 100644 --- a/core/fpdfapi/page/cpdf_shadingobject.cpp +++ b/core/fpdfapi/page/cpdf_shadingobject.cpp @@ -9,9 +9,10 @@ #include "core/fpdfapi/page/cpdf_shadingpattern.h" #include "core/fpdfapi/parser/cpdf_document.h" -CPDF_ShadingObject::CPDF_ShadingObject(CPDF_ShadingPattern* pattern, +CPDF_ShadingObject::CPDF_ShadingObject(int32_t content_stream, + CPDF_ShadingPattern* pattern, const CFX_Matrix& matrix) - : m_pShading(pattern), m_Matrix(matrix) {} + : CPDF_PageObject(content_stream), m_pShading(pattern), m_Matrix(matrix) {} CPDF_ShadingObject::~CPDF_ShadingObject() {} diff --git a/core/fpdfapi/page/cpdf_shadingobject.h b/core/fpdfapi/page/cpdf_shadingobject.h index 80e062c729..69b606749d 100644 --- a/core/fpdfapi/page/cpdf_shadingobject.h +++ b/core/fpdfapi/page/cpdf_shadingobject.h @@ -15,7 +15,9 @@ class CPDF_ShadingPattern; class CPDF_ShadingObject : public CPDF_PageObject { public: - CPDF_ShadingObject(CPDF_ShadingPattern* pattern, const CFX_Matrix& matrix); + CPDF_ShadingObject(int32_t content_stream, + CPDF_ShadingPattern* pattern, + const CFX_Matrix& matrix); ~CPDF_ShadingObject() override; // CPDF_PageObject: diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp index 7562fb37a6..0cc81f1e9b 100644 --- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp +++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp @@ -785,7 +785,8 @@ void CPDF_StreamContentParser::AddForm(CPDF_Stream* pStream) { CFX_Matrix matrix = m_pCurStates->m_CTM; matrix.Concat(m_mtContentToUser); - auto pFormObj = pdfium::MakeUnique(std::move(form), matrix); + auto pFormObj = pdfium::MakeUnique(GetCurrentStreamIndex(), + std::move(form), matrix); if (!m_pObjectHolder->BackgroundAlphaNeeded() && pFormObj->form()->BackgroundAlphaNeeded()) { m_pObjectHolder->SetBackgroundAlphaNeeded(true); @@ -800,14 +801,16 @@ CPDF_ImageObject* CPDF_StreamContentParser::AddImage( if (!pStream) return nullptr; - auto pImageObj = pdfium::MakeUnique(); + auto pImageObj = + pdfium::MakeUnique(GetCurrentStreamIndex()); pImageObj->SetImage( pdfium::MakeRetain(m_pDocument.Get(), std::move(pStream))); return AddImageObject(std::move(pImageObj)); } CPDF_ImageObject* CPDF_StreamContentParser::AddImage(uint32_t streamObjNum) { - auto pImageObj = pdfium::MakeUnique(); + auto pImageObj = + pdfium::MakeUnique(GetCurrentStreamIndex()); pImageObj->SetImage(m_pDocument->LoadImageFromPageData(streamObjNum)); return AddImageObject(std::move(pImageObj)); } @@ -817,7 +820,8 @@ CPDF_ImageObject* CPDF_StreamContentParser::AddImage( if (!pImage) return nullptr; - auto pImageObj = pdfium::MakeUnique(); + auto pImageObj = + pdfium::MakeUnique(GetCurrentStreamIndex()); pImageObj->SetImage( m_pDocument->GetPageData()->GetImage(pImage->GetStream()->GetObjNum())); @@ -1084,7 +1088,8 @@ void CPDF_StreamContentParser::Handle_ShadeFill() { CFX_Matrix matrix = m_pCurStates->m_CTM; matrix.Concat(m_mtContentToUser); - auto pObj = pdfium::MakeUnique(pShading, matrix); + auto pObj = pdfium::MakeUnique(GetCurrentStreamIndex(), + pShading, matrix); SetGraphicStates(pObj.get(), false, false, false); CFX_FloatRect bbox = pObj->m_ClipPath.HasRef() ? pObj->m_ClipPath.GetClipBox() : m_BBox; @@ -1219,7 +1224,7 @@ void CPDF_StreamContentParser::AddTextObject(ByteString* pStrs, pFont->IsType3Font() ? TextRenderingMode::MODE_FILL : m_pCurStates->m_TextState.GetTextMode(); { - auto pText = pdfium::MakeUnique(); + auto pText = pdfium::MakeUnique(GetCurrentStreamIndex()); m_pLastTextObject = pText.get(); SetGraphicStates(m_pLastTextObject.Get(), true, true, true); if (TextRenderingModeIsStrokeMode(text_mode)) { @@ -1258,6 +1263,12 @@ void CPDF_StreamContentParser::AddTextObject(ByteString* pStrs, } } +int32_t CPDF_StreamContentParser::GetCurrentStreamIndex() { + auto it = std::upper_bound(m_StreamStartOffsets.begin(), + m_StreamStartOffsets.end(), m_pSyntax->GetPos()); + return (it - m_StreamStartOffsets.begin()) - 1; +} + void CPDF_StreamContentParser::Handle_ShowText() { ByteString str = GetString(0); if (str.IsEmpty()) { @@ -1456,7 +1467,8 @@ void CPDF_StreamContentParser::AddPathObject(int FillType, bool bStroke) { CFX_Matrix matrix = m_pCurStates->m_CTM; matrix.Concat(m_mtContentToUser); if (bStroke || FillType) { - auto pPathObj = pdfium::MakeUnique(); + auto pPathObj = + pdfium::MakeUnique(GetCurrentStreamIndex()); pPathObj->m_bStroke = bStroke; pPathObj->m_FillType = FillType; pPathObj->m_Path = Path; @@ -1474,22 +1486,27 @@ void CPDF_StreamContentParser::AddPathObject(int FillType, bool bStroke) { } } -uint32_t CPDF_StreamContentParser::Parse(const uint8_t* pData, - uint32_t dwSize, - uint32_t max_cost) { +uint32_t CPDF_StreamContentParser::Parse( + const uint8_t* pData, + uint32_t dwSize, + uint32_t max_cost, + const std::vector& stream_start_offsets) { if (m_ParsedSet->size() > kMaxFormLevel || pdfium::ContainsKey(*m_ParsedSet, pData)) return dwSize; + m_StreamStartOffsets = stream_start_offsets; + pdfium::ScopedSetInsertion scopedInsert(m_ParsedSet.Get(), pData); - uint32_t InitObjCount = m_pObjectHolder->GetPageObjectList()->size(); + uint32_t init_obj_count = m_pObjectHolder->GetPageObjectList()->size(); CPDF_StreamParser syntax(pdfium::make_span(pData, dwSize), m_pDocument->GetByteStringPool()); CPDF_StreamParserAutoClearer auto_clearer(&m_pSyntax, &syntax); while (1) { - uint32_t cost = m_pObjectHolder->GetPageObjectList()->size() - InitObjCount; + uint32_t cost = + m_pObjectHolder->GetPageObjectList()->size() - init_obj_count; if (max_cost && cost >= max_cost) { break; } diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.h b/core/fpdfapi/page/cpdf_streamcontentparser.h index 438be024cc..adcb2a5b47 100644 --- a/core/fpdfapi/page/cpdf_streamcontentparser.h +++ b/core/fpdfapi/page/cpdf_streamcontentparser.h @@ -43,7 +43,10 @@ class CPDF_StreamContentParser { std::set* parsedSet); ~CPDF_StreamContentParser(); - uint32_t Parse(const uint8_t* pData, uint32_t dwSize, uint32_t max_cost); + uint32_t Parse(const uint8_t* pData, + uint32_t dwSize, + uint32_t max_cost, + const std::vector& stream_start_offsets); CPDF_PageObjectHolder* GetPageObjectHolder() const { return m_pObjectHolder.Get(); } @@ -127,6 +130,7 @@ class CPDF_StreamContentParser { std::vector GetColors() const; std::vector GetNamedColors() const; + int32_t GetCurrentStreamIndex(); void Handle_CloseFillStrokePath(); void Handle_FillStrokePath(); @@ -230,6 +234,7 @@ class CPDF_StreamContentParser { std::vector> m_StateStack; float m_Type3Data[6]; ContentParam m_ParamBuf[kParamBufSize]; + std::vector m_StreamStartOffsets; }; #endif // CORE_FPDFAPI_PAGE_CPDF_STREAMCONTENTPARSER_H_ diff --git a/core/fpdfapi/page/cpdf_textobject.cpp b/core/fpdfapi/page/cpdf_textobject.cpp index 36a4722773..e678d5fc10 100644 --- a/core/fpdfapi/page/cpdf_textobject.cpp +++ b/core/fpdfapi/page/cpdf_textobject.cpp @@ -18,7 +18,10 @@ CPDF_TextObjectItem::CPDF_TextObjectItem() : m_CharCode(0) {} CPDF_TextObjectItem::~CPDF_TextObjectItem() = default; -CPDF_TextObject::CPDF_TextObject() {} +CPDF_TextObject::CPDF_TextObject(int32_t content_stream) + : CPDF_PageObject(content_stream) {} + +CPDF_TextObject::CPDF_TextObject() : CPDF_TextObject(kNoContentStream) {} CPDF_TextObject::~CPDF_TextObject() { // Move m_CharCodes to a local variable so it will be captured in crash dumps, diff --git a/core/fpdfapi/page/cpdf_textobject.h b/core/fpdfapi/page/cpdf_textobject.h index a6fc62369c..d3b6dcc3de 100644 --- a/core/fpdfapi/page/cpdf_textobject.h +++ b/core/fpdfapi/page/cpdf_textobject.h @@ -25,6 +25,7 @@ class CPDF_TextObjectItem { class CPDF_TextObject : public CPDF_PageObject { public: + explicit CPDF_TextObject(int32_t content_stream); CPDF_TextObject(); ~CPDF_TextObject() override; diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp index dc61c0d974..3d2e090a36 100644 --- a/fpdfsdk/fpdf_edit_embeddertest.cpp +++ b/fpdfsdk/fpdf_edit_embeddertest.cpp @@ -9,6 +9,7 @@ #include "core/fpdfapi/font/cpdf_font.h" #include "core/fpdfapi/page/cpdf_page.h" +#include "core/fpdfapi/page/cpdf_pageobject.h" #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_number.h" @@ -655,6 +656,34 @@ TEST_F(FPDFEditEmbeddertest, DISABLED_RemoveExistingPageObject) { CloseSavedDocument(); } +// TODO(pdfium:1051): Extend this test to remove some elements and verify +// saving works. +TEST_F(FPDFEditEmbeddertest, GetContentStream) { + // Load document with some text split across streams. + EXPECT_TRUE(OpenDocument("split_streams.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Content stream 0: page objects 0-14. + // Content stream 1: page objects 15-17. + // Content stream 2: page object 18. + ASSERT_EQ(19, FPDFPage_CountObjects(page)); + for (int i = 0; i < 19; i++) { + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i); + ASSERT_TRUE(page_object); + CPDF_PageObject* cpdf_page_object = + CPDFPageObjectFromFPDFPageObject(page_object); + if (i < 15) + EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i; + else if (i < 18) + EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i; + else + EXPECT_EQ(2, cpdf_page_object->GetContentStream()) << i; + } + + UnloadPage(page); +} + TEST_F(FPDFEditEmbeddertest, InsertPageObjectAndSave) { // Load document with some text. EXPECT_TRUE(OpenDocument("hello_world.pdf")); diff --git a/testing/resources/split_streams.in b/testing/resources/split_streams.in new file mode 100644 index 0000000000..b13476936b --- /dev/null +++ b/testing/resources/split_streams.in @@ -0,0 +1,124 @@ +{{header}} +{{object 1 0}} +<< /Pages 2 0 R /Type /Catalog >> +endobj +{{object 2 0}} +<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >> +endobj +{{object 3 0}} +<< + /Contents 4 0 R + /Parent 2 0 R + /Resources << + /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >> + /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >> + >> + /Type /Page +>> +endobj +{{object 4 0}} +[ 12 0 R 13 0 R 14 0 R ] +endobj +{{object 5 0}} +<< /BM /Normal /CA 1 /ca 1 >> +endobj +{{object 6 0}} +<< /ca 0.705882 >> +endobj +{{object 7 0}} +<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >> +endobj +{{object 8 0}} +<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >> +endobj +{{object 9 0}} +<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >> +endobj +{{object 10 0}} +<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >> +endobj +{{object 12 0}} +<< {{streamlen}} >> +stream +q +0 0 0 RG 0 0 0 rg 1 w 0 J 0 j +/FXE1 gs +q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q +q 0 0 0.0509804 rg /FXE2 gs +BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642 +Tm /FXF2 9 Tf (Test 2) Tj ET Q +q 0 0 0.101961 rg /FXE2 gs +BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227 +Tm /FXF1 9 Tf (Test 3) Tj ET Q +q 0 0 0.156863 rg /FXE2 gs +BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613 +Tm /FXF2 9 Tf (Test 4) Tj ET Q +q 0 0 0.207843 rg /FXE2 gs +BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303 +Tm /FXF1 9 Tf (Test 5) Tj ET Q +q 0 0 0.262745 rg /FXE2 gs +BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231 +Tm /FXF2 9 Tf (Test 6) Tj ET Q +q 0 0 0.313726 rg /FXE2 gs +BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007 +Tm /FXF1 9 Tf (Test 7) Tj ET Q +q 0 0 0.364706 rg /FXE2 gs +BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644 +Tm /FXF2 9 Tf (Test 8) Tj ET Q +q 0 0 0.419608 rg /FXE2 gs +BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731 +Tm /FXF1 9 Tf (Test 9) Tj ET Q +q 0 0 0.470588 rg /FXE2 gs +BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488 +Tm /FXF2 9 Tf (Test 10) Tj ET Q +q 0 0 0.52549 rg /FXE2 gs +BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024 +Tm /FXF1 9 Tf (Test 11) Tj ET Q +q 0 0 0.576471 rg /FXE2 gs +BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03 +Tm /FXF2 9 Tf (Test 12) Tj ET Q +q 0 0 0.631373 rg /FXE2 gs +BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008 +Tm /FXF1 9 Tf (Test 13) Tj ET Q +q 0 0 0.682353 rg /FXE2 gs +BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847 +Tm /FXF2 9 Tf (Test 14) Tj ET Q +q 0 0 0.733333 rg /FXE2 gs +BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618 +Tm /FXF1 9 Tf (Test 15) Tj ET Q +endstream +endobj + +{{object 13 0}} +<< {{streamlen}} >> +stream +q 0 0 0.788235 rg /FXE2 gs +BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694 +Tm /FXF2 9 Tf (Test 16) Tj ET Q +q 0 0 0.839216 rg /FXE2 gs +BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843 +Tm /FXF1 9 Tf (Test 17) Tj ET Q +q 0 0 0.894118 rg /FXE2 gs +BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275 +Tm /FXF2 9 Tf (Test 18) Tj ET Q +endstream +endobj + +{{object 14 0}} +<< {{streamlen}} >> +stream +q 0 0 0.945098 rg /FXE2 gs +BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646 +Tm /FXF1 9 Tf (Test 19) Tj ET Q +Q +endstream +endobj + +{{xref}} +trailer << + /Root 1 0 R + /Size 15 + /ID [] +>> +{{startxref}} +%%EOF diff --git a/testing/resources/split_streams.pdf b/testing/resources/split_streams.pdf new file mode 100644 index 0000000000..d1768caa28 --- /dev/null +++ b/testing/resources/split_streams.pdf @@ -0,0 +1,142 @@ +%PDF-1.7 +% ò¤ô +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +2 0 obj +<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >> +endobj +3 0 obj +<< + /Contents 4 0 R + /Parent 2 0 R + /Resources << + /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >> + /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >> + >> + /Type /Page +>> +endobj +4 0 obj +[ 12 0 R 13 0 R 14 0 R ] +endobj +5 0 obj +<< /BM /Normal /CA 1 /ca 1 >> +endobj +6 0 obj +<< /ca 0.705882 >> +endobj +7 0 obj +<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >> +endobj +8 0 obj +<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >> +endobj +9 0 obj +<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >> +endobj +10 0 obj +<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >> +endobj +12 0 obj +<< /Length 1699 >> +stream +q +0 0 0 RG 0 0 0 rg 1 w 0 J 0 j +/FXE1 gs +q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q +q 0 0 0.0509804 rg /FXE2 gs +BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642 +Tm /FXF2 9 Tf (Test 2) Tj ET Q +q 0 0 0.101961 rg /FXE2 gs +BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227 +Tm /FXF1 9 Tf (Test 3) Tj ET Q +q 0 0 0.156863 rg /FXE2 gs +BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613 +Tm /FXF2 9 Tf (Test 4) Tj ET Q +q 0 0 0.207843 rg /FXE2 gs +BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303 +Tm /FXF1 9 Tf (Test 5) Tj ET Q +q 0 0 0.262745 rg /FXE2 gs +BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231 +Tm /FXF2 9 Tf (Test 6) Tj ET Q +q 0 0 0.313726 rg /FXE2 gs +BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007 +Tm /FXF1 9 Tf (Test 7) Tj ET Q +q 0 0 0.364706 rg /FXE2 gs +BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644 +Tm /FXF2 9 Tf (Test 8) Tj ET Q +q 0 0 0.419608 rg /FXE2 gs +BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731 +Tm /FXF1 9 Tf (Test 9) Tj ET Q +q 0 0 0.470588 rg /FXE2 gs +BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488 +Tm /FXF2 9 Tf (Test 10) Tj ET Q +q 0 0 0.52549 rg /FXE2 gs +BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024 +Tm /FXF1 9 Tf (Test 11) Tj ET Q +q 0 0 0.576471 rg /FXE2 gs +BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03 +Tm /FXF2 9 Tf (Test 12) Tj ET Q +q 0 0 0.631373 rg /FXE2 gs +BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008 +Tm /FXF1 9 Tf (Test 13) Tj ET Q +q 0 0 0.682353 rg /FXE2 gs +BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847 +Tm /FXF2 9 Tf (Test 14) Tj ET Q +q 0 0 0.733333 rg /FXE2 gs +BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618 +Tm /FXF1 9 Tf (Test 15) Tj ET Q +endstream +endobj + +13 0 obj +<< /Length 333 >> +stream +q 0 0 0.788235 rg /FXE2 gs +BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694 +Tm /FXF2 9 Tf (Test 16) Tj ET Q +q 0 0 0.839216 rg /FXE2 gs +BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843 +Tm /FXF1 9 Tf (Test 17) Tj ET Q +q 0 0 0.894118 rg /FXE2 gs +BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275 +Tm /FXF2 9 Tf (Test 18) Tj ET Q +endstream +endobj + +14 0 obj +<< /Length 115 >> +stream +q 0 0 0.945098 rg /FXE2 gs +BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646 +Tm /FXF1 9 Tf (Test 19) Tj ET Q +Q +endstream +endobj + +xref +0 15 +0000000000 65535 f +0000000015 00000 n +0000000064 00000 n +0000000149 00000 n +0000000345 00000 n +0000000385 00000 n +0000000430 00000 n +0000000464 00000 n +0000000536 00000 n +0000000606 00000 n +0000000679 00000 n +0000000000 65535 f +0000000751 00000 n +0000002503 00000 n +0000002888 00000 n +trailer << + /Root 1 0 R + /Size 15 + /ID [] +>> +startxref +3055 +%%EOF -- cgit v1.2.3