Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2018 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 7 | #include "tests/Test.h" |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 8 | |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 9 | #include "include/core/SkCanvas.h" |
| 10 | #include "include/core/SkFont.h" |
| 11 | #include "include/core/SkStream.h" |
| 12 | #include "include/docs/SkPDFDocument.h" |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 13 | |
| 14 | using PDFTag = SkPDF::StructureElementNode; |
| 15 | |
| 16 | // Test building a tagged PDF. |
| 17 | // Add this to args.gn to output the PDF to a file: |
| 18 | // extra_cflags = [ "-DSK_PDF_TEST_TAGS_OUTPUT_PATH=\"/tmp/foo.pdf\"" ] |
| 19 | DEF_TEST(SkPDF_tagged, r) { |
Hal Canary | 0f88861 | 2018-10-04 13:34:44 -0400 | [diff] [blame] | 20 | REQUIRE_PDF_DOCUMENT(SkPDF_tagged, r); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 21 | #ifdef SK_PDF_TEST_TAGS_OUTPUT_PATH |
| 22 | SkFILEWStream outputStream(SK_PDF_TEST_TAGS_OUTPUT_PATH); |
| 23 | #else |
| 24 | SkDynamicMemoryWStream outputStream; |
| 25 | #endif |
| 26 | |
| 27 | SkSize pageSize = SkSize::Make(612, 792); // U.S. Letter |
| 28 | |
| 29 | SkPDF::Metadata metadata; |
| 30 | metadata.fTitle = "Example Tagged PDF"; |
| 31 | metadata.fCreator = "Skia"; |
| 32 | SkTime::DateTime now; |
| 33 | SkTime::GetDateTime(&now); |
| 34 | metadata.fCreation = now; |
| 35 | metadata.fModified = now; |
| 36 | |
| 37 | // The document tag. |
| 38 | PDFTag root; |
| 39 | root.fNodeId = 1; |
| 40 | root.fType = SkPDF::DocumentStructureType::kDocument; |
| 41 | root.fChildCount = 5; |
| 42 | PDFTag rootChildren[5]; |
| 43 | root.fChildren = rootChildren; |
| 44 | |
| 45 | // Heading. |
| 46 | PDFTag& h1 = rootChildren[0]; |
| 47 | h1.fNodeId = 2; |
| 48 | h1.fType = SkPDF::DocumentStructureType::kH1; |
| 49 | h1.fChildCount = 0; |
| 50 | |
| 51 | // Initial paragraph. |
| 52 | PDFTag& p = rootChildren[1]; |
| 53 | p.fNodeId = 3; |
| 54 | p.fType = SkPDF::DocumentStructureType::kP; |
| 55 | p.fChildCount = 0; |
| 56 | |
| 57 | // Hidden div. This is never referenced by marked content |
| 58 | // so it should not appear in the resulting PDF. |
| 59 | PDFTag& div = rootChildren[2]; |
| 60 | div.fNodeId = 4; |
| 61 | div.fType = SkPDF::DocumentStructureType::kDiv; |
| 62 | div.fChildCount = 0; |
| 63 | |
| 64 | // A bulleted list of two items. |
| 65 | PDFTag& l = rootChildren[3]; |
| 66 | l.fNodeId = 5; |
| 67 | l.fType = SkPDF::DocumentStructureType::kL; |
| 68 | l.fChildCount = 4; |
| 69 | PDFTag listChildren[4]; |
| 70 | l.fChildren = listChildren; |
| 71 | |
| 72 | PDFTag& lm1 = listChildren[0]; |
| 73 | lm1.fNodeId = 6; |
| 74 | lm1.fType = SkPDF::DocumentStructureType::kLbl; |
| 75 | lm1.fChildCount = 0; |
| 76 | PDFTag& li1 = listChildren[1]; |
| 77 | li1.fNodeId = 7; |
| 78 | li1.fType = SkPDF::DocumentStructureType::kLI; |
| 79 | li1.fChildCount = 0; |
| 80 | |
| 81 | PDFTag& lm2 = listChildren[2]; |
| 82 | lm2.fNodeId = 8; |
| 83 | lm2.fType = SkPDF::DocumentStructureType::kLbl; |
| 84 | lm2.fChildCount = 0; |
| 85 | PDFTag& li2 = listChildren[3]; |
| 86 | li2.fNodeId = 9; |
| 87 | li2.fType = SkPDF::DocumentStructureType::kLI; |
| 88 | li2.fChildCount = 0; |
| 89 | |
| 90 | // Paragraph spanning two pages. |
| 91 | PDFTag& p2 = rootChildren[4]; |
| 92 | p2.fNodeId = 10; |
| 93 | p2.fType = SkPDF::DocumentStructureType::kP; |
| 94 | p2.fChildCount = 0; |
| 95 | |
| 96 | metadata.fStructureElementTreeRoot = &root; |
| 97 | sk_sp<SkDocument> document = SkPDF::MakeDocument( |
| 98 | &outputStream, metadata); |
| 99 | |
| 100 | SkPaint paint; |
| 101 | paint.setColor(SK_ColorBLACK); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 102 | |
| 103 | // First page. |
| 104 | SkCanvas* canvas = |
| 105 | document->beginPage(pageSize.width(), |
| 106 | pageSize.height()); |
| 107 | SkPDF::SetNodeId(canvas, 2); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 108 | SkFont font(nullptr, 36); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 109 | const char* message = "This is the title"; |
| 110 | canvas->translate(72, 72); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 111 | canvas->drawString(message, 0, 0, font, paint); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 112 | |
| 113 | SkPDF::SetNodeId(canvas, 3); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 114 | font.setSize(14); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 115 | message = "This is a simple paragraph."; |
| 116 | canvas->translate(0, 72); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 117 | canvas->drawString(message, 0, 0, font, paint); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 118 | |
| 119 | SkPDF::SetNodeId(canvas, 6); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 120 | font.setSize(14); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 121 | message = "*"; |
| 122 | canvas->translate(0, 72); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 123 | canvas->drawString(message, 0, 0, font, paint); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 124 | |
| 125 | SkPDF::SetNodeId(canvas, 7); |
| 126 | message = "List item 1"; |
| 127 | canvas->translate(36, 0); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 128 | canvas->drawString(message, 0, 0, font, paint); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 129 | |
| 130 | SkPDF::SetNodeId(canvas, 8); |
| 131 | message = "*"; |
| 132 | canvas->translate(-36, 36); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 133 | canvas->drawString(message, 0, 0, font, paint); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 134 | |
| 135 | SkPDF::SetNodeId(canvas, 9); |
| 136 | message = "List item 2"; |
| 137 | canvas->translate(36, 0); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 138 | canvas->drawString(message, 0, 0, font, paint); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 139 | |
| 140 | SkPDF::SetNodeId(canvas, 10); |
| 141 | message = "This is a paragraph that starts on one page"; |
| 142 | canvas->translate(-36, 6 * 72); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 143 | canvas->drawString(message, 0, 0, font, paint); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 144 | |
| 145 | document->endPage(); |
| 146 | |
| 147 | // Second page. |
| 148 | canvas = document->beginPage(pageSize.width(), |
| 149 | pageSize.height()); |
| 150 | SkPDF::SetNodeId(canvas, 10); |
| 151 | message = "and finishes on the second page."; |
| 152 | canvas->translate(72, 72); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 153 | canvas->drawString(message, 0, 0, font, paint); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 154 | |
| 155 | // This has a node ID but never shows up in the tag tree so it |
| 156 | // won't be tagged. |
| 157 | SkPDF::SetNodeId(canvas, 999); |
| 158 | message = "Page 2"; |
| 159 | canvas->translate(468, -36); |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 160 | canvas->drawString(message, 0, 0, font, paint); |
Dominic Mazzoni | 656cefe | 2018-09-25 20:29:15 -0700 | [diff] [blame] | 161 | |
| 162 | document->endPage(); |
| 163 | |
| 164 | document->close(); |
| 165 | |
| 166 | outputStream.flush(); |
| 167 | } |