Expose pdfium functionality for N-up mojo service.
Added two new functions to expose pdfium functionality to the to-be-created N-up conversion mojo service. ConvertPdfPagesToNupPdf will take multiple PDF pages and import them into a N-up PDF. ConvertPdfDocumentToNupPdf will take an entire PDF document and convert it to a N-up PDF document. In printing, 2-up, 3-up, or more generally N-up refers to a page layout strategy in which multiple pages are composited onto a single page. Bug: 775999 Change-Id: I11edc90ee717fcb295a35e3784f4ed4e72ae8117 Reviewed-on: https://chromium-review.googlesource.com/1134484 Commit-Queue: Shirleen Lou <xlou@chromium.org> Reviewed-by: Lei Zhang <thestig@chromium.org> Cr-Commit-Position: refs/heads/master@{#577052}
This commit is contained in:
35
pdf/pdf.cc
35
pdf/pdf.cc
@ -6,6 +6,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
@ -143,4 +145,37 @@ bool RenderPDFPageToBitmap(const void* pdf_buffer,
|
||||
pdf_buffer, pdf_buffer_size, page_number, settings, bitmap_buffer);
|
||||
}
|
||||
|
||||
bool ConvertPdfPagesToNupPdf(
|
||||
std::vector<base::span<const uint8_t>> input_buffers,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size) {
|
||||
ScopedSdkInitializer scoped_sdk_initializer;
|
||||
if (!scoped_sdk_initializer.Init())
|
||||
return false;
|
||||
|
||||
PDFEngineExports* engine_exports = PDFEngineExports::Get();
|
||||
return engine_exports->ConvertPdfPagesToNupPdf(
|
||||
std::move(input_buffers), pages_per_sheet, page_size_width,
|
||||
page_size_height, dest_pdf_buffer, dest_pdf_buffer_size);
|
||||
}
|
||||
|
||||
bool ConvertPdfDocumentToNupPdf(base::span<const uint8_t> input_buffer,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size) {
|
||||
ScopedSdkInitializer scoped_sdk_initializer;
|
||||
if (!scoped_sdk_initializer.Init())
|
||||
return false;
|
||||
|
||||
PDFEngineExports* engine_exports = PDFEngineExports::Get();
|
||||
return engine_exports->ConvertPdfDocumentToNupPdf(
|
||||
input_buffer, pages_per_sheet, page_size_width, page_size_height,
|
||||
dest_pdf_buffer, dest_pdf_buffer_size);
|
||||
}
|
||||
|
||||
} // namespace chrome_pdf
|
||||
|
56
pdf/pdf.h
56
pdf/pdf.h
@ -5,6 +5,9 @@
|
||||
#ifndef PDF_PDF_H_
|
||||
#define PDF_PDF_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/span.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
@ -126,6 +129,59 @@ bool RenderPDFPageToBitmap(const void* pdf_buffer,
|
||||
bool autorotate,
|
||||
bool use_color);
|
||||
|
||||
// Convert multiple PDF pages into a N-up PDF.
|
||||
// |input_buffers| is the vector of buffers with each buffer contains a PDF.
|
||||
// If any of the PDFs contains multiple pages, only the first page of the
|
||||
// document is used.
|
||||
// |pages_per_sheet| is the number of pages to put on one sheet.
|
||||
// |page_size_width| is the width of the output page size, measured in PDF
|
||||
// "user space" units.
|
||||
// |page_size_height| is the height of the output page size, measured in PDF
|
||||
// "user space" units.
|
||||
// |dest_pdf_buffer| is the output N-up PDF page. Caller takes ownership, and
|
||||
// needs to free the memory.
|
||||
// |dest_pdf_buffer_size| is the size of output N-up PDF page.
|
||||
//
|
||||
// |page_size_width| and |page_size_height| are the print media size. The page
|
||||
// size of the output N-up PDF is determined by the |pages_per_sheet|, the
|
||||
// orientation of the PDF pages contained in the |input_buffers|, and the media
|
||||
// page size |page_size_width| and |page_size_height|. For example, when
|
||||
// |page_size_width| = 512, |page_size_height| = 792, |pages_per_sheet| = 2, and
|
||||
// the orientation of |input_buffers| = portrait, the output N-up PDF will have
|
||||
// |page_size_width| = 792, and |page_size_height| = 512.
|
||||
// See printing::NupParameters for more details on how the output page
|
||||
// orientation is determined, to understand why |page_size_width| and
|
||||
// |page_size_height| may be swapped in some cases.
|
||||
bool ConvertPdfPagesToNupPdf(
|
||||
std::vector<base::span<const uint8_t>> input_buffers,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size);
|
||||
|
||||
// Convert a PDF document to a N-up PDF document.
|
||||
// |input_buffer| is the buffer that contains the entire PDF document to be
|
||||
// converted to a N-up PDF document.
|
||||
// |pages_per_sheet| is the number of pages to put on one sheet.
|
||||
// |page_size_width| is the width of the media page size, measured in PDF
|
||||
// "user space" units.
|
||||
// |page_size_height| is the height of the media page size, measured in PDF
|
||||
// "user space" units.
|
||||
// |dest_pdf_buffer| is the output N-up PDF page. Caller takes ownership, and
|
||||
// needs to free the memory.
|
||||
// |dest_pdf_buffer_size| is the size of output N-up PDF document.
|
||||
//
|
||||
// Refer to the description of ConvertPdfPagesToNupPdf to understand how the
|
||||
// output page size |page_size_width| and |page_size_height| will be calculated.
|
||||
// The algorithm used to determine the output page size is the same.
|
||||
bool ConvertPdfDocumentToNupPdf(base::span<const uint8_t> input_buffer,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size);
|
||||
|
||||
} // namespace chrome_pdf
|
||||
|
||||
#endif // PDF_PDF_H_
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/span.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/time/time.h"
|
||||
@ -464,6 +465,24 @@ class PDFEngineExports {
|
||||
const RenderingSettings& settings,
|
||||
void* bitmap_buffer) = 0;
|
||||
|
||||
// See the definition of ConvertPdfPagesToNupPdf in pdf.cc for details.
|
||||
virtual bool ConvertPdfPagesToNupPdf(
|
||||
std::vector<base::span<const uint8_t>> input_buffers,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size) = 0;
|
||||
|
||||
// See the definition of ConvertPdfDocumentToNupPdf in pdf.cc for details.
|
||||
virtual bool ConvertPdfDocumentToNupPdf(
|
||||
base::span<const uint8_t> input_buffer,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size) = 0;
|
||||
|
||||
virtual bool GetPDFDocInfo(const void* pdf_buffer,
|
||||
int buffer_size,
|
||||
int* page_count,
|
||||
|
@ -8,8 +8,13 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "pdf/pdfium/pdfium_mem_buffer_file_write.h"
|
||||
#include "pdf/pdfium/pdfium_print.h"
|
||||
#include "printing/nup_parameters.h"
|
||||
#include "printing/units.h"
|
||||
#include "third_party/pdfium/public/cpp/fpdf_scopers.h"
|
||||
#include "third_party/pdfium/public/fpdf_ppo.h"
|
||||
#include "third_party/pdfium/public/fpdfview.h"
|
||||
|
||||
using printing::ConvertUnitDouble;
|
||||
@ -89,6 +94,66 @@ int CalculatePosition(FPDF_PAGE page,
|
||||
return rotate;
|
||||
}
|
||||
|
||||
ScopedFPDFDocument LoadPdfData(base::span<const uint8_t> pdf_buffer) {
|
||||
ScopedFPDFDocument doc;
|
||||
if (base::IsValueInRangeForNumericType<int>(pdf_buffer.size())) {
|
||||
doc.reset(
|
||||
FPDF_LoadMemDocument(pdf_buffer.data(), pdf_buffer.size(), nullptr));
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
ScopedFPDFDocument CreatePdfDoc(
|
||||
std::vector<base::span<const uint8_t>> input_buffers) {
|
||||
ScopedFPDFDocument doc(FPDF_CreateNewDocument());
|
||||
|
||||
size_t index = 0;
|
||||
for (auto input_buffer : input_buffers) {
|
||||
ScopedFPDFDocument single_page_doc = LoadPdfData(input_buffer);
|
||||
if (!FPDF_ImportPages(doc.get(), single_page_doc.get(), "1", index++)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
bool CreateNupPdfDocument(FPDF_DOCUMENT doc,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size) {
|
||||
if (!dest_pdf_buffer || !dest_pdf_buffer_size)
|
||||
return false;
|
||||
|
||||
printing::NupParameters nup_params;
|
||||
bool is_landscape = PDFiumPrint::IsSourcePdfLandscape(doc);
|
||||
nup_params.SetParameters(pages_per_sheet, is_landscape);
|
||||
bool paper_is_landscape = page_size_width > page_size_height;
|
||||
if (nup_params.landscape() != paper_is_landscape)
|
||||
std::swap(page_size_width, page_size_height);
|
||||
|
||||
ScopedFPDFDocument output_doc_nup(FPDF_ImportNPagesToOne(
|
||||
doc, page_size_width, page_size_height, nup_params.num_pages_on_x_axis(),
|
||||
nup_params.num_pages_on_y_axis()));
|
||||
|
||||
if (!output_doc_nup)
|
||||
return false;
|
||||
|
||||
// Copy data to the |dest_pdf_buffer| here.
|
||||
// TODO(thestig): Avoid this copy.
|
||||
PDFiumMemBufferFileWrite output_file_write;
|
||||
if (!FPDF_SaveAsCopy(output_doc_nup.get(), &output_file_write, 0)) {
|
||||
return false;
|
||||
}
|
||||
*dest_pdf_buffer = malloc(output_file_write.size());
|
||||
memcpy(*dest_pdf_buffer, output_file_write.buffer().c_str(),
|
||||
output_file_write.size());
|
||||
*dest_pdf_buffer_size = output_file_write.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PDFEngineExports::RenderingSettings::RenderingSettings(int dpi_x,
|
||||
@ -245,6 +310,38 @@ bool PDFiumEngineExports::RenderPDFPageToBitmap(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PDFiumEngineExports::ConvertPdfPagesToNupPdf(
|
||||
std::vector<base::span<const uint8_t>> input_buffers,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size) {
|
||||
ScopedFPDFDocument doc = CreatePdfDoc(std::move(input_buffers));
|
||||
if (!doc)
|
||||
return false;
|
||||
|
||||
return CreateNupPdfDocument(doc.get(), pages_per_sheet, page_size_width,
|
||||
page_size_height, dest_pdf_buffer,
|
||||
dest_pdf_buffer_size);
|
||||
}
|
||||
|
||||
bool PDFiumEngineExports::ConvertPdfDocumentToNupPdf(
|
||||
base::span<const uint8_t> input_buffer,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size) {
|
||||
ScopedFPDFDocument doc = LoadPdfData(input_buffer);
|
||||
if (!doc)
|
||||
return false;
|
||||
|
||||
return CreateNupPdfDocument(doc.get(), pages_per_sheet, page_size_width,
|
||||
page_size_height, dest_pdf_buffer,
|
||||
dest_pdf_buffer_size);
|
||||
}
|
||||
|
||||
bool PDFiumEngineExports::GetPDFDocInfo(const void* pdf_buffer,
|
||||
int buffer_size,
|
||||
int* page_count,
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/containers/span.h"
|
||||
#include "build/build_config.h"
|
||||
#include "pdf/pdf_engine.h"
|
||||
|
||||
@ -36,6 +37,19 @@ class PDFiumEngineExports : public PDFEngineExports {
|
||||
int page_number,
|
||||
const RenderingSettings& settings,
|
||||
void* bitmap_buffer) override;
|
||||
bool ConvertPdfPagesToNupPdf(
|
||||
std::vector<base::span<const uint8_t>> input_buffers,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size) override;
|
||||
bool ConvertPdfDocumentToNupPdf(base::span<const uint8_t> input_buffer,
|
||||
size_t pages_per_sheet,
|
||||
size_t page_size_width,
|
||||
size_t page_size_height,
|
||||
void** dest_pdf_buffer,
|
||||
size_t* dest_pdf_buffer_size) override;
|
||||
bool GetPDFDocInfo(const void* pdf_buffer,
|
||||
int buffer_size,
|
||||
int* page_count,
|
||||
|
@ -98,4 +98,74 @@ TEST_F(PDFiumEngineExportsTest, GetPDFPageSizeByIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PDFiumEngineExportsTest, ConvertPdfPagesToNupPdf) {
|
||||
base::FilePath pdf_path =
|
||||
pdf_data_dir().Append(FILE_PATH_LITERAL("rectangles.pdf"));
|
||||
std::string pdf_data;
|
||||
ASSERT_TRUE(base::ReadFileToString(pdf_path, &pdf_data));
|
||||
|
||||
std::vector<base::span<const uint8_t>> pdf_buffers;
|
||||
|
||||
EXPECT_FALSE(
|
||||
ConvertPdfPagesToNupPdf(pdf_buffers, 1, 512, 792, nullptr, nullptr));
|
||||
|
||||
pdf_buffers.push_back(base::as_bytes(base::make_span(pdf_data)));
|
||||
pdf_buffers.push_back(base::as_bytes(base::make_span(pdf_data)));
|
||||
|
||||
void* output_pdf_buffer;
|
||||
size_t output_pdf_buffer_size;
|
||||
ASSERT_TRUE(ConvertPdfPagesToNupPdf(
|
||||
pdf_buffers, 2, 512, 792, &output_pdf_buffer, &output_pdf_buffer_size));
|
||||
ASSERT_GT(output_pdf_buffer_size, 0U);
|
||||
ASSERT_NE(output_pdf_buffer, nullptr);
|
||||
int page_count;
|
||||
ASSERT_TRUE(GetPDFDocInfo(output_pdf_buffer, output_pdf_buffer_size,
|
||||
&page_count, nullptr));
|
||||
ASSERT_EQ(1, page_count);
|
||||
|
||||
double width;
|
||||
double height;
|
||||
ASSERT_TRUE(GetPDFPageSizeByIndex(output_pdf_buffer, output_pdf_buffer_size,
|
||||
0, &width, &height));
|
||||
EXPECT_DOUBLE_EQ(792.0, width);
|
||||
EXPECT_DOUBLE_EQ(512.0, height);
|
||||
|
||||
free(output_pdf_buffer);
|
||||
}
|
||||
|
||||
TEST_F(PDFiumEngineExportsTest, ConvertPdfDocumentToNupPdf) {
|
||||
base::FilePath pdf_path =
|
||||
pdf_data_dir().Append(FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
|
||||
std::string pdf_data;
|
||||
ASSERT_TRUE(base::ReadFileToString(pdf_path, &pdf_data));
|
||||
|
||||
base::span<const uint8_t> pdf_buffer;
|
||||
|
||||
EXPECT_FALSE(
|
||||
ConvertPdfDocumentToNupPdf(pdf_buffer, 1, 512, 792, nullptr, nullptr));
|
||||
|
||||
pdf_buffer = base::as_bytes(base::make_span(pdf_data));
|
||||
|
||||
void* output_pdf_buffer;
|
||||
size_t output_pdf_buffer_size;
|
||||
ASSERT_TRUE(ConvertPdfDocumentToNupPdf(
|
||||
pdf_buffer, 4, 512, 792, &output_pdf_buffer, &output_pdf_buffer_size));
|
||||
ASSERT_GT(output_pdf_buffer_size, 0U);
|
||||
ASSERT_NE(output_pdf_buffer, nullptr);
|
||||
int page_count;
|
||||
ASSERT_TRUE(GetPDFDocInfo(output_pdf_buffer, output_pdf_buffer_size,
|
||||
&page_count, nullptr));
|
||||
ASSERT_EQ(2, page_count);
|
||||
for (int page_number = 0; page_number < page_count; ++page_number) {
|
||||
double width;
|
||||
double height;
|
||||
ASSERT_TRUE(GetPDFPageSizeByIndex(output_pdf_buffer, output_pdf_buffer_size,
|
||||
page_number, &width, &height));
|
||||
EXPECT_DOUBLE_EQ(512.0, width);
|
||||
EXPECT_DOUBLE_EQ(792.0, height);
|
||||
}
|
||||
|
||||
free(output_pdf_buffer);
|
||||
}
|
||||
|
||||
} // namespace chrome_pdf
|
||||
|
@ -37,24 +37,6 @@ bool ShouldDoNup(int pages_per_sheet) {
|
||||
return pages_per_sheet > 1;
|
||||
}
|
||||
|
||||
// Check the source doc orientation. Returns true if the doc is landscape.
|
||||
// For now the orientation of the doc is determined by its first page's
|
||||
// orientation. Improvement can be added in the future to better determine the
|
||||
// orientation of the source docs that have mixed orientation.
|
||||
// TODO(xlou): rotate pages if the source doc has mixed orientation. So that
|
||||
// the orientation of all pages of the doc are uniform. Pages of square size
|
||||
// will not be rotated.
|
||||
bool IsSourcePdfLandscape(FPDF_DOCUMENT doc) {
|
||||
DCHECK(doc);
|
||||
|
||||
ScopedFPDFPage pdf_page(FPDF_LoadPage(doc, 0));
|
||||
DCHECK(pdf_page);
|
||||
|
||||
bool is_source_landscape =
|
||||
FPDF_GetPageWidth(pdf_page.get()) > FPDF_GetPageHeight(pdf_page.get());
|
||||
return is_source_landscape;
|
||||
}
|
||||
|
||||
// Set the destination page size and content area in points based on source
|
||||
// page rotation and orientation.
|
||||
//
|
||||
@ -235,6 +217,17 @@ std::vector<uint32_t> PDFiumPrint::GetPageNumbersFromPrintPageNumberRange(
|
||||
return page_numbers;
|
||||
}
|
||||
|
||||
bool PDFiumPrint::IsSourcePdfLandscape(FPDF_DOCUMENT doc) {
|
||||
DCHECK(doc);
|
||||
|
||||
ScopedFPDFPage pdf_page(FPDF_LoadPage(doc, 0));
|
||||
DCHECK(pdf_page);
|
||||
|
||||
bool is_source_landscape =
|
||||
FPDF_GetPageWidth(pdf_page.get()) > FPDF_GetPageHeight(pdf_page.get());
|
||||
return is_source_landscape;
|
||||
}
|
||||
|
||||
pp::Buffer_Dev PDFiumPrint::PrintPagesAsRasterPDF(
|
||||
const PP_PrintPageNumberRange_Dev* page_ranges,
|
||||
uint32_t page_range_count,
|
||||
|
@ -40,6 +40,15 @@ class PDFiumPrint {
|
||||
const PP_PrintSettings_Dev& print_settings,
|
||||
const PP_PdfPrintSettings_Dev& pdf_print_settings);
|
||||
|
||||
// Check the source doc orientation. Returns true if the doc is landscape.
|
||||
// For now the orientation of the doc is determined by its first page's
|
||||
// orientation. Improvement can be added in the future to better determine
|
||||
// the orientation of the source docs that have mixed orientation.
|
||||
// TODO(xlou): rotate pages if the source doc has mixed orientation. So that
|
||||
// the orientation of all pages of the doc are uniform. Pages of square size
|
||||
// will not be rotated.
|
||||
static bool IsSourcePdfLandscape(FPDF_DOCUMENT doc);
|
||||
|
||||
private:
|
||||
FPDF_DOCUMENT CreateSinglePageRasterPdf(
|
||||
double source_page_width,
|
||||
|
50
pdf/test/data/rectangles.pdf
Normal file
50
pdf/test/data/rectangles.pdf
Normal file
@ -0,0 +1,50 @@
|
||||
%PDF-1.7
|
||||
%<25><><EFBFBD><EFBFBD>
|
||||
1 0 obj <<
|
||||
/Type /Catalog
|
||||
/Pages 2 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj <<
|
||||
/Type /Pages
|
||||
/MediaBox [ 0 0 200 300 ]
|
||||
/Count 1
|
||||
/Kids [ 3 0 R ]
|
||||
>>
|
||||
endobj
|
||||
3 0 obj <<
|
||||
/Type /Page
|
||||
/Parent 2 0 R
|
||||
/Contents 4 0 R
|
||||
>>
|
||||
endobj
|
||||
4 0 obj <<
|
||||
>>
|
||||
stream
|
||||
q
|
||||
0 0 0 rg
|
||||
0 290 10 10 re B*
|
||||
10 150 50 30 re B*
|
||||
0 0 1 rg
|
||||
190 290 10 10 re B*
|
||||
70 232 50 30 re B*
|
||||
0 1 0 rg
|
||||
190 0 10 10 re B*
|
||||
130 150 50 30 re B*
|
||||
1 0 0 rg
|
||||
0 0 10 10 re B*
|
||||
70 67 50 30 re B*
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
xref
|
||||
0 5
|
||||
0000000000 65535 f
|
||||
0000000015 00000 n
|
||||
0000000068 00000 n
|
||||
0000000161 00000 n
|
||||
0000000230 00000 n
|
||||
trailer<< /Root 1 0 R /Size 5 >>
|
||||
startxref
|
||||
456
|
||||
%%EOF
|
122
pdf/test/data/rectangles_multi_pages.pdf
Normal file
122
pdf/test/data/rectangles_multi_pages.pdf
Normal file
@ -0,0 +1,122 @@
|
||||
%PDF-1.7
|
||||
%<25><><EFBFBD><EFBFBD>
|
||||
1 0 obj <<
|
||||
/Type /Catalog
|
||||
/Pages 2 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj <<
|
||||
/Type /Pages
|
||||
/MediaBox [ 0 0 200 250 ]
|
||||
/Count 5
|
||||
/Kids [ 3 0 R 5 0 R 7 0 R 9 0 R 11 0 R ]
|
||||
>>
|
||||
endobj
|
||||
3 0 obj <<
|
||||
/Type /Page
|
||||
/Parent 2 0 R
|
||||
/Contents 4 0 R
|
||||
>>
|
||||
endobj
|
||||
4 0 obj <<
|
||||
/Length 49
|
||||
>>
|
||||
stream
|
||||
q
|
||||
1 1 0 rg
|
||||
100 0 30 50 re B*
|
||||
70 67 50 30 re B*
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
5 0 obj <<
|
||||
/Type /Page
|
||||
/Parent 2 0 R
|
||||
/Rotate 90
|
||||
/Contents 6 0 R
|
||||
>>
|
||||
endobj
|
||||
6 0 obj <<
|
||||
/Length 49
|
||||
>>
|
||||
stream
|
||||
q
|
||||
0 1 1 rg
|
||||
100 0 30 50 re B*
|
||||
70 67 50 30 re B*
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
7 0 obj <<
|
||||
/Type /Page
|
||||
/Parent 2 0 R
|
||||
/Contents 8 0 R
|
||||
>>
|
||||
endobj
|
||||
8 0 obj <<
|
||||
/Length 49
|
||||
>>
|
||||
stream
|
||||
q
|
||||
1 0 0 rg
|
||||
100 0 30 50 re B*
|
||||
70 67 50 30 re B*
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
9 0 obj <<
|
||||
/Type /Page
|
||||
/Parent 2 0 R
|
||||
/Contents 10 0 R
|
||||
>>
|
||||
endobj
|
||||
10 0 obj <<
|
||||
/Length 51
|
||||
>>
|
||||
stream
|
||||
q
|
||||
0 1 0 rg
|
||||
100 0 30 50 re B*
|
||||
100 150 50 30 re B*
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
11 0 obj <<
|
||||
/Type /Page
|
||||
/Parent 2 0 R
|
||||
/Contents 12 0 R
|
||||
>>
|
||||
endobj
|
||||
12 0 obj <<
|
||||
/Length 50
|
||||
>>
|
||||
stream
|
||||
q
|
||||
0 0 0 rg
|
||||
0 90 80 60 re B*
|
||||
100 150 50 30 re B*
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
xref
|
||||
0 13
|
||||
0000000000 65535 f
|
||||
0000000015 00000 n
|
||||
0000000068 00000 n
|
||||
0000000186 00000 n
|
||||
0000000255 00000 n
|
||||
0000000355 00000 n
|
||||
0000000437 00000 n
|
||||
0000000537 00000 n
|
||||
0000000606 00000 n
|
||||
0000000706 00000 n
|
||||
0000000776 00000 n
|
||||
0000000879 00000 n
|
||||
0000000950 00000 n
|
||||
trailer <<
|
||||
/Root 1 0 R
|
||||
/Size 13
|
||||
>>
|
||||
startxref
|
||||
1052
|
||||
%%EOF
|
Reference in New Issue
Block a user