You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PDFLogicalStructureHandler.java 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.render.pdf;
  19. import java.util.Locale;
  20. import org.apache.fop.pdf.PDFArray;
  21. import org.apache.fop.pdf.PDFDictionary;
  22. import org.apache.fop.pdf.PDFDocument;
  23. import org.apache.fop.pdf.PDFLink;
  24. import org.apache.fop.pdf.PDFName;
  25. import org.apache.fop.pdf.PDFPage;
  26. import org.apache.fop.pdf.PDFParentTree;
  27. import org.apache.fop.pdf.PDFStructElem;
  28. import org.apache.fop.pdf.PDFStructTreeRoot;
  29. /**
  30. * Handles the creation of the logical structure in the PDF document.
  31. */
  32. class PDFLogicalStructureHandler {
  33. private static final PDFName MCR = new PDFName("MCR");
  34. private static final PDFName OBJR = new PDFName("OBJR");
  35. private static final MarkedContentInfo ARTIFACT = new MarkedContentInfo(null, -1, null);
  36. private final PDFDocument pdfDoc;
  37. private final PDFParentTree parentTree = new PDFParentTree();
  38. private int parentTreeKey;
  39. private PDFPage currentPage;
  40. /**
  41. * The array of references, from marked-content sequences in the current
  42. * page, to their parent structure elements. This will be a value in the
  43. * structure parent tree, whose corresponding key will be the page's
  44. * StructParents entry.
  45. */
  46. private PDFArray pageParentTreeArray;
  47. private PDFStructElem rootStructureElement;
  48. /**
  49. * Class providing the necessary information for bracketing content
  50. * associated to a structure element as a marked-content sequence.
  51. */
  52. static final class MarkedContentInfo {
  53. /**
  54. * A value that can be used for the tag operand of a marked-content
  55. * operator. This is the structure type of the corresponding structure
  56. * element.
  57. */
  58. final String tag; // CSOK: VisibilityModifier
  59. /**
  60. * The value for the MCID entry of the marked-content sequence's property list.
  61. */
  62. final int mcid; // CSOK: VisibilityModifier
  63. private final PDFStructElem parent;
  64. private MarkedContentInfo(String tag, int mcid, PDFStructElem parent) {
  65. this.tag = tag;
  66. this.mcid = mcid;
  67. this.parent = parent;
  68. }
  69. }
  70. /**
  71. * Creates a new instance for handling the logical structure of the given document.
  72. *
  73. * @param pdfDoc a document
  74. */
  75. PDFLogicalStructureHandler(PDFDocument pdfDoc) {
  76. this.pdfDoc = pdfDoc;
  77. PDFStructTreeRoot structTreeRoot = pdfDoc.getFactory().makeStructTreeRoot(parentTree);
  78. rootStructureElement = pdfDoc.getFactory().makeStructureElement(
  79. FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot);
  80. structTreeRoot.addKid(rootStructureElement);
  81. }
  82. PDFStructElem createPageSequence(Locale language) {
  83. PDFStructElem structElemPart = pdfDoc.getFactory().makeStructureElement(
  84. FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement),
  85. rootStructureElement);
  86. rootStructureElement.addKid(structElemPart);
  87. if (language != null) {
  88. structElemPart.setLanguage(language);
  89. }
  90. return structElemPart;
  91. }
  92. private int getNextParentTreeKey() {
  93. return parentTreeKey++;
  94. }
  95. /**
  96. * Receive notification of the beginning of a new page.
  97. *
  98. * @param page the page that will be rendered in PDF
  99. */
  100. void startPage(PDFPage page) {
  101. currentPage = page;
  102. currentPage.setStructParents(getNextParentTreeKey());
  103. pageParentTreeArray = new PDFArray();
  104. }
  105. /**
  106. * Receive notification of the end of the current page.
  107. */
  108. void endPage() {
  109. // TODO
  110. // Values in a number tree must be indirect references to the PDF
  111. // objects associated to the keys. To enforce that the array is
  112. // registered to the PDF document. Unfortunately that can't be done
  113. // earlier since a call to PDFContentGenerator.flushPDFDoc can be made
  114. // before the array is complete, which would result in only part of it
  115. // being output to the PDF.
  116. // This should really be handled by PDFNumsArray
  117. pdfDoc.registerObject(pageParentTreeArray);
  118. parentTree.getNums().put(currentPage.getStructParents(), pageParentTreeArray);
  119. }
  120. private MarkedContentInfo addToParentTree(PDFStructElem structureTreeElement) {
  121. PDFStructElem parent = (structureTreeElement instanceof PDFStructElem.Placeholder)
  122. ? structureTreeElement.getParentStructElem()
  123. : structureTreeElement;
  124. pageParentTreeArray.add(parent);
  125. String type = parent.getStructureType().toString();
  126. int mcid = pageParentTreeArray.length() - 1;
  127. return new MarkedContentInfo(type, mcid, structureTreeElement);
  128. }
  129. /**
  130. * Adds a content item corresponding to text into the structure tree, if
  131. * there is a structure element associated to it.
  132. *
  133. * @param structElem the parent structure element of the piece of text
  134. * @return the necessary information for bracketing the content as a
  135. * marked-content sequence. If there is no element in the structure tree
  136. * associated to that content, returns an instance whose
  137. * {@link MarkedContentInfo#tag} value is <code>null</code>. The content
  138. * must then be treated as an artifact.
  139. */
  140. MarkedContentInfo addTextContentItem(PDFStructElem structElem) {
  141. if (structElem == null) {
  142. return ARTIFACT;
  143. } else {
  144. MarkedContentInfo mci = addToParentTree(structElem);
  145. PDFDictionary contentItem = new PDFDictionary();
  146. contentItem.put("Type", MCR);
  147. contentItem.put("Pg", this.currentPage);
  148. contentItem.put("MCID", mci.mcid);
  149. mci.parent.addKid(contentItem);
  150. return mci;
  151. }
  152. }
  153. /**
  154. * Adds a content item corresponding to an image into the structure tree, if
  155. * there is a structure element associated to it.
  156. *
  157. * @param structElem the parent structure element of the image
  158. * @return the necessary information for bracketing the content as a
  159. * marked-content sequence. If there is no element in the structure tree
  160. * associated to that image, returns an instance whose
  161. * {@link MarkedContentInfo#tag} value is <code>null</code>. The image must
  162. * then be treated as an artifact.
  163. */
  164. MarkedContentInfo addImageContentItem(PDFStructElem structElem) {
  165. if (structElem == null) {
  166. return ARTIFACT;
  167. } else {
  168. MarkedContentInfo mci = addToParentTree(structElem);
  169. mci.parent.setMCIDKid(mci.mcid);
  170. mci.parent.setPage(this.currentPage);
  171. return mci;
  172. }
  173. }
  174. /**
  175. * Adds a content item corresponding to the given link into the structure
  176. * tree.
  177. *
  178. * @param link a link
  179. * @param structureTreeElement its parent structure element
  180. */
  181. void addLinkContentItem(PDFLink link, PDFStructElem structureTreeElement) {
  182. int structParent = getNextParentTreeKey();
  183. link.setStructParent(structParent);
  184. PDFDictionary contentItem = new PDFDictionary();
  185. contentItem.put("Type", OBJR);
  186. contentItem.put("Pg", this.currentPage);
  187. contentItem.put("Obj", link);
  188. parentTree.getNums().put(structParent, structureTreeElement);
  189. structureTreeElement.addKid(contentItem);
  190. }
  191. }