| /* | |
| * Copyright 2020 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "tests/Test.h" | |
| #ifdef SK_SUPPORT_PDF | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkDocument.h" | |
| #include "include/core/SkFont.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkTypeface.h" | |
| #include "include/docs/SkPDFDocument.h" | |
| #include "include/docs/SkPDFJpegHelpers.h" | |
| #include "src/pdf/SkPDFUtils.h" | |
| #include "tools/fonts/FontToolUtils.h" | |
| #include <array> | |
| #include <memory> | |
| #include <utility> | |
| #include <vector> | |
| using PDFTag = SkPDF::StructureElementNode; | |
| // Test building a tagged PDF containing a table. | |
| // Add this to args.gn to output the PDF to a file: | |
| // extra_cflags = [ "-DSK_PDF_TEST_TAGS_OUTPUT_PATH=\"/tmp/table.pdf\"" ] | |
| DEF_TEST(SkPDF_tagged_table, r) { | |
| REQUIRE_PDF_DOCUMENT(SkPDF_tagged, r); | |
| #ifdef SK_PDF_TEST_TAGS_OUTPUT_PATH | |
| SkFILEWStream outputStream(SK_PDF_TEST_TAGS_OUTPUT_PATH); | |
| #else | |
| SkDynamicMemoryWStream outputStream; | |
| #endif | |
| SkSize pageSize = SkSize::Make(612, 792); // U.S. Letter | |
| SkPDF::Metadata metadata; | |
| metadata.fTitle = "Example Tagged Table PDF"; | |
| metadata.fCreator = "Skia"; | |
| SkPDF::DateTime now; | |
| SkPDFUtils::GetDateTime(&now); | |
| metadata.fCreation = now; | |
| metadata.fModified = now; | |
| metadata.jpegDecoder = SkPDF::JPEG::Decode; | |
| metadata.jpegEncoder = SkPDF::JPEG::Encode; | |
| constexpr int kRowCount = 5; | |
| constexpr int kColCount = 4; | |
| constexpr std::array<const char*, kRowCount * kColCount> cellData = { | |
| "Car Make and Model", "Engine", "City MPG", "Highway MPG", | |
| "Mitsubishi Mirage ES", "Gas", "28", "47", | |
| "Toyota Prius Three", "Hybrid", "43", "59", | |
| "Nissan Leaf SL", "Electric", "N/A", nullptr, | |
| "Tesla Model 3", nullptr, "N/A", nullptr}; | |
| // The document tag. | |
| auto root = std::make_unique<PDFTag>(); | |
| root->fNodeId = 1; | |
| root->fTypeString = "Document"; | |
| root->fLang = "en-US"; | |
| // Heading. | |
| auto h1 = std::make_unique<PDFTag>(); | |
| h1->fNodeId = 2; | |
| h1->fTypeString = "H1"; | |
| h1->fAlt = "Tagged PDF Table Alt Text"; | |
| root->fChildVector.push_back(std::move(h1)); | |
| // Table. | |
| auto table = std::make_unique<PDFTag>(); | |
| table->fNodeId = 3; | |
| table->fTypeString = "Table"; | |
| auto& rows = table->fChildVector; | |
| table->fAttributes.appendFloatArray("Layout", "BBox", {72, 72, 360, 360}); | |
| table->fAttributes.appendTextString("Table", "Summary", "Fuel efficiency"); | |
| for (int rowIndex = 0; rowIndex < kRowCount; rowIndex++) { | |
| auto row = std::make_unique<PDFTag>(); | |
| row->fNodeId = 4 + rowIndex; | |
| row->fTypeString = "TR"; | |
| auto& cells = row->fChildVector; | |
| for (int colIndex = 0; colIndex < kColCount; colIndex++) { | |
| auto cell = std::make_unique<PDFTag>(); | |
| int cellIndex = rowIndex * kColCount + colIndex; | |
| cell->fNodeId = 10 + cellIndex; | |
| if (!cellData[cellIndex]) { | |
| cell->fTypeString = "NonStruct"; | |
| } else if (rowIndex == 0 || colIndex == 0) { | |
| cell->fTypeString = "TH"; | |
| cell->fAttributes.appendTextString("Table", "Short", SkString("Car")); | |
| } else { | |
| cell->fTypeString = "TD"; | |
| std::vector<int> headerIds; | |
| headerIds.push_back(10 + rowIndex * kColCount); // Row header | |
| headerIds.push_back(10 + colIndex); // Col header. | |
| cell->fAttributes.appendNodeIdArray( | |
| "Table", "Headers", headerIds); | |
| } | |
| if (cellIndex == 13) { | |
| cell->fAttributes.appendInt("Table", "RowSpan", 2); | |
| } else if (cellIndex == 14 || cellIndex == 18) { | |
| cell->fAttributes.appendInt("Table", "ColSpan", 2); | |
| } else if (rowIndex == 0 || colIndex == 0) { | |
| cell->fAttributes.appendName( | |
| "Table", "Scope", rowIndex == 0 ? "Column" : "Row"); | |
| } | |
| cells.push_back(std::move(cell)); | |
| } | |
| rows.push_back(std::move(row)); | |
| } | |
| root->fChildVector.push_back(std::move(table)); | |
| metadata.fStructureElementTreeRoot = root.get(); | |
| sk_sp<SkDocument> document = SkPDF::MakeDocument( | |
| &outputStream, metadata); | |
| SkPaint paint; | |
| paint.setColor(SK_ColorBLACK); | |
| SkCanvas* canvas = | |
| document->beginPage(pageSize.width(), | |
| pageSize.height()); | |
| SkPDF::SetNodeId(canvas, 2); | |
| SkFont font(ToolUtils::DefaultTypeface(), 36); | |
| canvas->drawString("Tagged PDF Table", 72, 72, font, paint); | |
| font.setSize(14); | |
| for (int rowIndex = 0; rowIndex < kRowCount; rowIndex++) { | |
| for (int colIndex = 0; colIndex < kColCount; colIndex++) { | |
| int cellIndex = rowIndex * kColCount + colIndex; | |
| const char* str = cellData[cellIndex]; | |
| if (!str) | |
| continue; | |
| int x = 72 + colIndex * 108 + (colIndex > 0 ? 72 : 0); | |
| int y = 144 + rowIndex * 48; | |
| SkPDF::SetNodeId(canvas, 10 + cellIndex); | |
| canvas->drawString(str, x, y, font, paint); | |
| } | |
| } | |
| document->endPage(); | |
| document->close(); | |
| outputStream.flush(); | |
| } | |
| #endif |