Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

XWPFHeaderFooter.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  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. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.xwpf.usermodel;
  16. import java.io.IOException;
  17. import java.io.InputStream;
  18. import java.io.OutputStream;
  19. import java.util.ArrayList;
  20. import java.util.Collections;
  21. import java.util.List;
  22. import org.apache.poi.ooxml.POIXMLDocumentPart;
  23. import org.apache.poi.ooxml.POIXMLException;
  24. import org.apache.poi.ooxml.POIXMLRelation;
  25. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  26. import org.apache.poi.openxml4j.opc.PackagePart;
  27. import org.apache.poi.util.IOUtils;
  28. import org.apache.poi.util.Internal;
  29. import org.apache.xmlbeans.XmlCursor;
  30. import org.apache.xmlbeans.XmlObject;
  31. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr;
  32. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
  33. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
  34. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
  35. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
  36. /**
  37. * Parent of XWPF headers and footers
  38. */
  39. public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBody {
  40. List<XWPFParagraph> paragraphs = new ArrayList<>();
  41. List<XWPFTable> tables = new ArrayList<>();
  42. List<XWPFPictureData> pictures = new ArrayList<>();
  43. List<IBodyElement> bodyElements = new ArrayList<>();
  44. CTHdrFtr headerFooter;
  45. XWPFDocument document;
  46. XWPFHeaderFooter(XWPFDocument doc, CTHdrFtr hdrFtr) {
  47. if (doc == null) {
  48. throw new NullPointerException();
  49. }
  50. document = doc;
  51. headerFooter = hdrFtr;
  52. readHdrFtr();
  53. }
  54. protected XWPFHeaderFooter() {
  55. headerFooter = CTHdrFtr.Factory.newInstance();
  56. readHdrFtr();
  57. }
  58. /**
  59. * @since by POI 3.14-Beta1
  60. */
  61. public XWPFHeaderFooter(POIXMLDocumentPart parent, PackagePart part) {
  62. super(parent, part);
  63. this.document = (XWPFDocument) getParent();
  64. if (this.document == null) {
  65. throw new NullPointerException();
  66. }
  67. }
  68. @Override
  69. protected void onDocumentRead() throws IOException {
  70. for (POIXMLDocumentPart poixmlDocumentPart : getRelations()) {
  71. if (poixmlDocumentPart instanceof XWPFPictureData) {
  72. XWPFPictureData xwpfPicData = (XWPFPictureData) poixmlDocumentPart;
  73. pictures.add(xwpfPicData);
  74. document.registerPackagePictureData(xwpfPicData);
  75. }
  76. }
  77. }
  78. @Internal
  79. public CTHdrFtr _getHdrFtr() {
  80. return headerFooter;
  81. }
  82. @Override
  83. public List<IBodyElement> getBodyElements() {
  84. return Collections.unmodifiableList(bodyElements);
  85. }
  86. /**
  87. * Returns the paragraph(s) that holds
  88. * the text of the header or footer.
  89. * Normally there is only the one paragraph, but
  90. * there could be more in certain cases, or
  91. * a table.
  92. */
  93. @Override
  94. public List<XWPFParagraph> getParagraphs() {
  95. return Collections.unmodifiableList(paragraphs);
  96. }
  97. /**
  98. * Return the table(s) that holds the text
  99. * of the header or footer, for complex cases
  100. * where a paragraph isn't used.
  101. * Normally there's just one paragraph, but some
  102. * complex headers/footers have a table or two
  103. * in addition.
  104. */
  105. @Override
  106. public List<XWPFTable> getTables() throws ArrayIndexOutOfBoundsException {
  107. return Collections.unmodifiableList(tables);
  108. }
  109. /**
  110. * Returns the textual content of the header/footer,
  111. * by flattening out the text of its paragraph(s)
  112. */
  113. public String getText() {
  114. StringBuilder t = new StringBuilder(64);
  115. //TODO: simplify this to get ibody elements in order
  116. for (XWPFParagraph paragraph : paragraphs) {
  117. if (!paragraph.isEmpty()) {
  118. String text = paragraph.getText();
  119. if (text != null && text.length() > 0) {
  120. t.append(text);
  121. t.append('\n');
  122. }
  123. }
  124. }
  125. for (XWPFTable table : tables) {
  126. String text = table.getText();
  127. if (text != null && text.length() > 0) {
  128. t.append(text);
  129. t.append('\n');
  130. }
  131. }
  132. for (IBodyElement bodyElement : getBodyElements()) {
  133. if (bodyElement instanceof XWPFSDT) {
  134. t.append(((XWPFSDT) bodyElement).getContent().getText()).append('\n');
  135. }
  136. }
  137. return t.toString();
  138. }
  139. /**
  140. * set a new headerFooter
  141. */
  142. public void setHeaderFooter(CTHdrFtr headerFooter) {
  143. this.headerFooter = headerFooter;
  144. readHdrFtr();
  145. }
  146. /**
  147. * if there is a corresponding {@link XWPFTable} of the parameter ctTable in the tableList of this header
  148. * the method will return this table
  149. * if there is no corresponding {@link XWPFTable} the method will return null
  150. */
  151. @Override
  152. public XWPFTable getTable(CTTbl ctTable) {
  153. for (XWPFTable table : tables) {
  154. if (table == null)
  155. return null;
  156. if (table.getCTTbl().equals(ctTable))
  157. return table;
  158. }
  159. return null;
  160. }
  161. @Override
  162. public XWPFParagraph getParagraph(CTP p) {
  163. for (XWPFParagraph paragraph : paragraphs) {
  164. if (paragraph.getCTP().equals(p))
  165. return paragraph;
  166. }
  167. return null;
  168. }
  169. /**
  170. * Returns the paragraph that holds
  171. * the text of the header or footer.
  172. */
  173. @Override
  174. public XWPFParagraph getParagraphArray(int pos) {
  175. if(pos >= 0 && pos<paragraphs.size()){
  176. return paragraphs.get(pos);
  177. }
  178. return null;
  179. }
  180. /**
  181. * get a List of all Paragraphs
  182. *
  183. * @return a list of {@link XWPFParagraph}
  184. */
  185. public List<XWPFParagraph> getListParagraph() {
  186. return paragraphs;
  187. }
  188. public List<XWPFPictureData> getAllPictures() {
  189. return Collections.unmodifiableList(pictures);
  190. }
  191. /**
  192. * get all Pictures in this package
  193. *
  194. * @return all Pictures in this package
  195. */
  196. public List<XWPFPictureData> getAllPackagePictures() {
  197. return document.getAllPackagePictures();
  198. }
  199. /**
  200. * Adds a picture to the document.
  201. *
  202. * @param pictureData The picture data
  203. * @param format The format of the picture.
  204. * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} .
  205. * @throws InvalidFormatException If the format of the picture is not known.
  206. */
  207. public String addPictureData(byte[] pictureData, int format) throws InvalidFormatException {
  208. XWPFPictureData xwpfPicData = document.findPackagePictureData(pictureData, format);
  209. POIXMLRelation relDesc = XWPFPictureData.RELATIONS[format];
  210. if (xwpfPicData == null) {
  211. /* Part doesn't exist, create a new one */
  212. int idx = document.getNextPicNameNumber(format);
  213. xwpfPicData = (XWPFPictureData) createRelationship(relDesc, XWPFFactory.getInstance(), idx);
  214. /* write bytes to new part */
  215. PackagePart picDataPart = xwpfPicData.getPackagePart();
  216. try (OutputStream out = picDataPart.getOutputStream()) {
  217. out.write(pictureData);
  218. } catch (IOException e) {
  219. throw new POIXMLException(e);
  220. }
  221. document.registerPackagePictureData(xwpfPicData);
  222. pictures.add(xwpfPicData);
  223. return getRelationId(xwpfPicData);
  224. } else if (!getRelations().contains(xwpfPicData)) {
  225. /*
  226. * Part already existed, but was not related so far. Create
  227. * relationship to the already existing part and update
  228. * POIXMLDocumentPart data.
  229. */
  230. // TODO add support for TargetMode.EXTERNAL relations.
  231. RelationPart rp = addRelation(null, XWPFRelation.IMAGES, xwpfPicData);
  232. pictures.add(xwpfPicData);
  233. return rp.getRelationship().getId();
  234. } else {
  235. /* Part already existed, get relation id and return it */
  236. return getRelationId(xwpfPicData);
  237. }
  238. }
  239. /**
  240. * Adds a picture to the document.
  241. *
  242. * @param is The stream to read image from
  243. * @param format The format of the picture.
  244. * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} .
  245. * @throws InvalidFormatException If the format of the picture is not known.
  246. * @throws IOException If reading the picture-data from the stream fails.
  247. */
  248. public String addPictureData(InputStream is, int format) throws InvalidFormatException, IOException {
  249. byte[] data = IOUtils.toByteArrayWithMaxLength(is, XWPFPictureData.getMaxImageSize());
  250. return addPictureData(data, format);
  251. }
  252. /**
  253. * returns the PictureData by blipID
  254. *
  255. * @return XWPFPictureData of a specificID
  256. */
  257. public XWPFPictureData getPictureDataByID(String blipID) {
  258. POIXMLDocumentPart relatedPart = getRelationById(blipID);
  259. if (relatedPart != null && relatedPart instanceof XWPFPictureData) {
  260. return (XWPFPictureData) relatedPart;
  261. }
  262. return null;
  263. }
  264. /**
  265. * Adds a new paragraph at the end of the header or footer
  266. *
  267. * @return new {@link XWPFParagraph} object
  268. */
  269. public XWPFParagraph createParagraph() {
  270. XWPFParagraph paragraph = new XWPFParagraph(headerFooter.addNewP(), this);
  271. paragraphs.add(paragraph);
  272. bodyElements.add(paragraph);
  273. return paragraph;
  274. }
  275. /**
  276. * Adds a new table at the end of the header or footer
  277. *
  278. * @param rows - number of rows in the table
  279. * @param cols - number of columns in the table
  280. * @return new {@link XWPFTable} object
  281. */
  282. public XWPFTable createTable(int rows, int cols) {
  283. XWPFTable table = new XWPFTable(headerFooter.addNewTbl(), this, rows, cols);
  284. tables.add(table);
  285. bodyElements.add(table);
  286. return table;
  287. }
  288. /**
  289. * Removes a specific paragraph from this header / footer
  290. *
  291. * @param paragraph - {@link XWPFParagraph} object to remove
  292. */
  293. public void removeParagraph(XWPFParagraph paragraph) {
  294. if (paragraphs.contains(paragraph)) {
  295. CTP ctP = paragraph.getCTP();
  296. try (XmlCursor c = ctP.newCursor()) {
  297. c.removeXml();
  298. }
  299. paragraphs.remove(paragraph);
  300. bodyElements.remove(paragraph);
  301. }
  302. }
  303. /**
  304. * Removes a specific table from this header / footer
  305. *
  306. * @param table - {@link XWPFTable} object to remove
  307. */
  308. public void removeTable(XWPFTable table) {
  309. if (tables.contains(table)) {
  310. CTTbl ctTbl = table.getCTTbl();
  311. try (XmlCursor c = ctTbl.newCursor()) {
  312. c.removeXml();
  313. }
  314. tables.remove(table);
  315. bodyElements.remove(table);
  316. }
  317. }
  318. /**
  319. * Clears all paragraphs and tables from this header / footer
  320. */
  321. public void clearHeaderFooter() {
  322. try (XmlCursor c = headerFooter.newCursor()) {
  323. c.removeXmlContents();
  324. }
  325. paragraphs.clear();
  326. tables.clear();
  327. bodyElements.clear();
  328. }
  329. /**
  330. * add a new paragraph at position of the cursor
  331. *
  332. * @return the inserted paragraph
  333. */
  334. @Override
  335. public XWPFParagraph insertNewParagraph(XmlCursor cursor) {
  336. if (isCursorInHdrF(cursor)) {
  337. String uri = CTP.type.getName().getNamespaceURI();
  338. String localPart = "p";
  339. cursor.beginElement(localPart, uri);
  340. cursor.toParent();
  341. CTP p = (CTP) cursor.getObject();
  342. XWPFParagraph newP = new XWPFParagraph(p, this);
  343. XmlObject o = null;
  344. while (!(o instanceof CTP) && (cursor.toPrevSibling())) {
  345. o = cursor.getObject();
  346. }
  347. if ((!(o instanceof CTP)) || o == p) {
  348. paragraphs.add(0, newP);
  349. } else {
  350. int pos = paragraphs.indexOf(getParagraph((CTP) o)) + 1;
  351. paragraphs.add(pos, newP);
  352. }
  353. int i = 0;
  354. try (final XmlCursor p2 = p.newCursor()) {
  355. cursor.toCursor(p2);
  356. }
  357. while (cursor.toPrevSibling()) {
  358. o = cursor.getObject();
  359. if (o instanceof CTP || o instanceof CTTbl)
  360. i++;
  361. }
  362. bodyElements.add(i, newP);
  363. try(final XmlCursor p3 = p.newCursor()) {
  364. cursor.toCursor(p3);
  365. cursor.toEndToken();
  366. }
  367. return newP;
  368. }
  369. return null;
  370. }
  371. /**
  372. * @return the inserted table
  373. */
  374. @Override
  375. public XWPFTable insertNewTbl(final XmlCursor cursor) {
  376. if (isCursorInHdrF(cursor)) {
  377. String uri = CTTbl.type.getName().getNamespaceURI();
  378. String localPart = "tbl";
  379. cursor.beginElement(localPart, uri);
  380. cursor.toParent();
  381. CTTbl t = (CTTbl) cursor.getObject();
  382. XWPFTable newT = new XWPFTable(t, this);
  383. cursor.removeXmlContents();
  384. XmlObject o = null;
  385. while (!(o instanceof CTTbl) && (cursor.toPrevSibling())) {
  386. o = cursor.getObject();
  387. }
  388. if (!(o instanceof CTTbl)) {
  389. tables.add(0, newT);
  390. } else {
  391. int pos = tables.indexOf(getTable((CTTbl) o)) + 1;
  392. tables.add(pos, newT);
  393. }
  394. int i = 0;
  395. try (final XmlCursor cursor2 = t.newCursor()) {
  396. while (cursor2.toPrevSibling()) {
  397. o = cursor2.getObject();
  398. if (o instanceof CTP || o instanceof CTTbl) {
  399. i++;
  400. }
  401. }
  402. }
  403. bodyElements.add(i, newT);
  404. try(final XmlCursor cursor3 = t.newCursor()) {
  405. cursor.toCursor(cursor3);
  406. cursor.toEndToken();
  407. }
  408. return newT;
  409. }
  410. return null;
  411. }
  412. /**
  413. * verifies that cursor is on the right position
  414. */
  415. private boolean isCursorInHdrF(XmlCursor cursor) {
  416. try (XmlCursor verify = cursor.newCursor()) {
  417. verify.toParent();
  418. boolean result = (verify.getObject() == this.headerFooter);
  419. return result;
  420. }
  421. }
  422. public POIXMLDocumentPart getOwner() {
  423. return this;
  424. }
  425. /**
  426. * Returns the table at position pos
  427. */
  428. @Override
  429. public XWPFTable getTableArray(int pos) {
  430. if (pos >= 0 && pos < tables.size()) {
  431. return tables.get(pos);
  432. }
  433. return null;
  434. }
  435. /**
  436. * inserts an existing XWPFTable to the arrays bodyElements and tables
  437. */
  438. @Override
  439. public void insertTable(int pos, XWPFTable table) {
  440. bodyElements.add(pos, table);
  441. int i = 0;
  442. for (CTTbl tbl : headerFooter.getTblArray()) {
  443. if (tbl == table.getCTTbl()) {
  444. break;
  445. }
  446. i++;
  447. }
  448. tables.add(i, table);
  449. }
  450. public void readHdrFtr() {
  451. bodyElements = new ArrayList<>();
  452. paragraphs = new ArrayList<>();
  453. tables = new ArrayList<>();
  454. // parse the document with cursor and add
  455. // the XmlObject to its lists
  456. try (XmlCursor cursor = headerFooter.newCursor()) {
  457. cursor.selectPath("./*");
  458. while (cursor.toNextSelection()) {
  459. XmlObject o = cursor.getObject();
  460. if (o instanceof CTP) {
  461. XWPFParagraph p = new XWPFParagraph((CTP) o, this);
  462. paragraphs.add(p);
  463. bodyElements.add(p);
  464. }
  465. if (o instanceof CTTbl) {
  466. XWPFTable t = new XWPFTable((CTTbl) o, this);
  467. tables.add(t);
  468. bodyElements.add(t);
  469. }
  470. }
  471. }
  472. }
  473. /**
  474. * get the TableCell which belongs to the TableCell
  475. */
  476. @Override
  477. public XWPFTableCell getTableCell(CTTc cell) {
  478. XmlObject o;
  479. CTRow row;
  480. try (XmlCursor cursor = cell.newCursor()) {
  481. cursor.toParent();
  482. o = cursor.getObject();
  483. if (!(o instanceof CTRow)) {
  484. return null;
  485. }
  486. row = (CTRow) o;
  487. cursor.toParent();
  488. o = cursor.getObject();
  489. }
  490. if (!(o instanceof CTTbl)) {
  491. return null;
  492. }
  493. CTTbl tbl = (CTTbl) o;
  494. XWPFTable table = getTable(tbl);
  495. if (table == null) {
  496. return null;
  497. }
  498. XWPFTableRow tableRow = table.getRow(row);
  499. return tableRow.getTableCell(cell);
  500. }
  501. @Override
  502. public XWPFDocument getXWPFDocument() {
  503. if (document != null) {
  504. return document;
  505. } else {
  506. return (XWPFDocument) getParent();
  507. }
  508. }
  509. public void setXWPFDocument(XWPFDocument doc) {
  510. document = doc;
  511. }
  512. /**
  513. * returns the Part, to which the body belongs, which you need for adding relationship to other parts
  514. */
  515. @Override
  516. public POIXMLDocumentPart getPart() {
  517. return this;
  518. }
  519. @Override
  520. protected void prepareForCommit() {
  521. // must contain at least an empty paragraph
  522. if (bodyElements.isEmpty()) {
  523. createParagraph();
  524. }
  525. // Cells must contain at least an empty paragraph
  526. for (XWPFTable tbl : tables) {
  527. for (XWPFTableRow row : tbl.tableRows) {
  528. for (XWPFTableCell cell : row.getTableCells()) {
  529. if (cell.getBodyElements().isEmpty()) {
  530. cell.addParagraph();
  531. }
  532. }
  533. }
  534. }
  535. super.prepareForCommit();
  536. }
  537. }