1 | /* | |
2 | * $Id: SAXiTextHandler.java 4070 2009-09-19 18:21:12Z psoares33 $ | |
3 | * | |
4 | * Copyright 2001, 2002 by Bruno Lowagie. | |
5 | * | |
6 | * The contents of this file are subject to the Mozilla Public License Version 1.1 | |
7 | * (the "License"); you may not use this file except in compliance with the License. | |
8 | * You may obtain a copy of the License at http://www.mozilla.org/MPL/ | |
9 | * | |
10 | * Software distributed under the License is distributed on an "AS IS" basis, | |
11 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
12 | * for the specific language governing rights and limitations under the License. | |
13 | * | |
14 | * The Original Code is 'iText, a free JAVA-PDF library'. | |
15 | * | |
16 | * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by | |
17 | * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. | |
18 | * All Rights Reserved. | |
19 | * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer | |
20 | * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. | |
21 | * | |
22 | * Contributor(s): all the names of the contributors are added in the source code | |
23 | * where applicable. | |
24 | * | |
25 | * Alternatively, the contents of this file may be used under the terms of the | |
26 | * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the | |
27 | * provisions of LGPL are applicable instead of those above. If you wish to | |
28 | * allow use of your version of this file only under the terms of the LGPL | |
29 | * License and not to allow others to use your version of this file under | |
30 | * the MPL, indicate your decision by deleting the provisions above and | |
31 | * replace them with the notice and other provisions required by the LGPL. | |
32 | * If you do not delete the provisions above, a recipient may use your version | |
33 | * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. | |
34 | * | |
35 | * This library is free software; you can redistribute it and/or modify it | |
36 | * under the terms of the MPL as stated above or under the terms of the GNU | |
37 | * Library General Public License as published by the Free Software Foundation; | |
38 | * either version 2 of the License, or any later version. | |
39 | * | |
40 | * This library is distributed in the hope that it will be useful, but WITHOUT | |
41 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
42 | * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more | |
43 | * details. | |
44 | * | |
45 | * If you didn't download this code from the following link, you should check if | |
46 | * you aren't using an obsolete version: | |
47 | * http://www.lowagie.com/iText/ | |
48 | */ | |
49 | ||
50 | package com.lowagie.text.xml; | |
51 | ||
52 | import com.lowagie.text.List; | |
53 | import com.lowagie.text.*; | |
54 | import com.lowagie.text.factories.ElementFactory; | |
55 | import com.lowagie.text.pdf.BaseFont; | |
56 | import com.lowagie.text.pdf.draw.LineSeparator; | |
57 | import com.lowagie.text.xml.simpleparser.EntitiesToSymbol; | |
58 | import org.xml.sax.Attributes; | |
59 | import org.xml.sax.helpers.DefaultHandler; | |
60 | ||
61 | import javax.annotation.Nullable; | |
62 | import java.lang.reflect.Field; | |
63 | import java.util.*; | |
64 | ||
65 | /** | |
66 | * This class is a Handler that controls the iText XML to PDF conversion. | |
67 | * Subclass it, if you want to change the way iText translates XML to PDF. | |
68 | */ | |
69 | ||
70 | public class SAXiTextHandler extends DefaultHandler { | |
71 | ||
72 | /** | |
73 | * This is the resulting document. | |
74 | */ | |
75 | protected DocListener document; | |
76 | ||
77 | /** | |
78 | * This is a <CODE>Stack</CODE> of objects, waiting to be added to the | |
79 | * document. | |
80 | */ | |
81 | protected Stack<Element> stack; | |
82 | ||
83 | /** | |
84 | * Counts the number of chapters in this document. | |
85 | */ | |
86 | protected int chapters = 0; | |
87 | ||
88 | /** | |
89 | * This is the current chunk to which characters can be added. | |
90 | */ | |
91 | protected Chunk currentChunk = null; | |
92 | ||
93 | /** | |
94 | * This is the current chunk to which characters can be added. | |
95 | */ | |
96 | protected boolean ignore = false; | |
97 | ||
98 | /** | |
99 | * This is a flag that can be set, if you want to open and close the | |
100 | * Document-object yourself. | |
101 | */ | |
102 | protected boolean controlOpenClose = true; | |
103 | /** | |
104 | * This hashmap contains all the custom keys and peers. | |
105 | */ | |
106 | protected HashMap myTags; | |
107 | /** | |
108 | * current margin of a page. | |
109 | */ | |
110 | float topMargin = 36; | |
111 | /** | |
112 | * current margin of a page. | |
113 | */ | |
114 | float rightMargin = 36; | |
115 | /** | |
116 | * current margin of a page. | |
117 | */ | |
118 | float leftMargin = 36; | |
119 | /** | |
120 | * current margin of a page. | |
121 | */ | |
122 | float bottomMargin = 36; | |
123 | private BaseFont bf = null; | |
124 | ||
125 | /** | |
126 | * @param document | |
127 | */ | |
128 | public SAXiTextHandler(DocListener document) { | |
129 | this.document = document; | |
130 | stack = new Stack<>(); | |
131 | } | |
132 | ||
133 | /** | |
134 | * @param document | |
135 | * @param myTags | |
136 | * @param bf | |
137 | */ | |
138 | public SAXiTextHandler(DocListener document, HashMap myTags, BaseFont bf) { | |
139 | this(document, myTags); | |
140 | this.bf = bf; | |
141 | } | |
142 | ||
143 | /** | |
144 | * @param document | |
145 | * @param myTags | |
146 | */ | |
147 | public SAXiTextHandler(DocListener document, HashMap myTags) { | |
148 | this(document); | |
149 | this.myTags = myTags; | |
150 | } | |
151 | ||
152 | /** | |
153 | * Sets the parameter that allows you to enable/disable the control over the | |
154 | * Document.open() and Document.close() method. | |
155 | * <p> | |
156 | * If you set this parameter to true (= default), the parser will open the | |
157 | * Document object when the start-root-tag is encountered and close it when | |
158 | * the end-root-tag is met. If you set it to false, you have to open and | |
159 | * close the Document object yourself. | |
160 | * | |
161 | * @param controlOpenClose set this to false if you plan to open/close the Document | |
162 | * yourself | |
163 | */ | |
164 | ||
165 | public void setControlOpenClose(boolean controlOpenClose) { | |
166 | this.controlOpenClose = controlOpenClose; | |
167 | } | |
168 | ||
169 | /** | |
170 | * This method gets called when a start tag is encountered. | |
171 | * | |
172 | * @param uri the Uniform Resource Identifier | |
173 | * @param localName the local name (without prefix), or the empty string if | |
174 | * Namespace processing is not being performed. | |
175 | * @param name the name of the tag that is encountered | |
176 | * @param attributes the list of attributes | |
177 | */ | |
178 | ||
179 | public void startElement(String uri, String localName, String name, @Nullable Attributes attributes) { | |
180 | ||
181 | Properties properties = new Properties(); | |
182 |
1
1. startElement : negated conditional → NO_COVERAGE |
if (attributes != null) { |
183 |
2
1. startElement : changed conditional boundary → NO_COVERAGE 2. startElement : negated conditional → NO_COVERAGE |
for (int i = 0; i < attributes.getLength(); i++) { |
184 | String attribute = attributes.getQName(i); | |
185 | properties.setProperty(attribute, attributes.getValue(i)); | |
186 | } | |
187 | } | |
188 |
1
1. startElement : removed call to com/lowagie/text/xml/SAXiTextHandler::handleStartingTags → NO_COVERAGE |
handleStartingTags(name, properties); |
189 | } | |
190 | ||
191 | /** | |
192 | * This method deals with the starting tags. | |
193 | * | |
194 | * @param name the name of the tag | |
195 | * @param attributes the list of attributes | |
196 | */ | |
197 | ||
198 | public void handleStartingTags(String name, Properties attributes) { | |
199 |
2
1. handleStartingTags : negated conditional → SURVIVED 2. handleStartingTags : negated conditional → SURVIVED |
if (ignore || ElementTags.IGNORE.equals(name)) { |
200 | ignore = true; | |
201 | return; | |
202 | } | |
203 | ||
204 | // maybe there is some meaningful data that wasn't between tags | |
205 |
2
1. handleStartingTags : negated conditional → KILLED 2. handleStartingTags : negated conditional → KILLED |
if (currentChunk != null && isNotBlank(currentChunk.getContent())) { |
206 | TextElementArray current; | |
207 | try { | |
208 | current = (TextElementArray) stack.pop(); | |
209 | } catch (EmptyStackException ese) { | |
210 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (bf == null) { |
211 | current = new Paragraph("", new Font()); | |
212 | } else { | |
213 | current = new Paragraph("", new Font(this.bf)); | |
214 | } | |
215 | } | |
216 | current.add(currentChunk); | |
217 | stack.push(current); | |
218 | currentChunk = null; | |
219 | } | |
220 | ||
221 | // chunks | |
222 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.CHUNK.equals(name)) { |
223 | currentChunk = ElementFactory.getChunk(attributes); | |
224 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (bf != null) { |
225 |
1
1. handleStartingTags : removed call to com/lowagie/text/Chunk::setFont → NO_COVERAGE |
currentChunk.setFont(new Font(this.bf)); |
226 | } | |
227 | return; | |
228 | } | |
229 | ||
230 | // symbols | |
231 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.ENTITY.equals(name)) { |
232 | Font f = new Font(); | |
233 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (currentChunk != null) { |
234 |
1
1. handleStartingTags : removed call to com/lowagie/text/xml/SAXiTextHandler::handleEndingTags → NO_COVERAGE |
handleEndingTags(ElementTags.CHUNK); |
235 | f = currentChunk.getFont(); | |
236 | } | |
237 | currentChunk = EntitiesToSymbol.get(attributes.getProperty(ElementTags.ID), | |
238 | f); | |
239 | return; | |
240 | } | |
241 | ||
242 | // phrases | |
243 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.PHRASE.equals(name)) { |
244 | stack.push(ElementFactory.getPhrase(attributes)); | |
245 | return; | |
246 | } | |
247 | ||
248 | // anchors | |
249 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.ANCHOR.equals(name)) { |
250 | stack.push(ElementFactory.getAnchor(attributes)); | |
251 | return; | |
252 | } | |
253 | ||
254 | // paragraphs and titles | |
255 |
2
1. handleStartingTags : negated conditional → KILLED 2. handleStartingTags : negated conditional → KILLED |
if (ElementTags.PARAGRAPH.equals(name) || ElementTags.TITLE.equals(name)) { |
256 | stack.push(ElementFactory.getParagraph(attributes)); | |
257 | return; | |
258 | } | |
259 | ||
260 | // lists | |
261 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.LIST.equals(name)) { |
262 | stack.push(ElementFactory.getList(attributes)); | |
263 | return; | |
264 | } | |
265 | ||
266 | // listitems | |
267 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.LISTITEM.equals(name)) { |
268 | stack.push(ElementFactory.getListItem(attributes)); | |
269 | return; | |
270 | } | |
271 | ||
272 | // cells | |
273 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.CELL.equals(name)) { |
274 | stack.push(ElementFactory.getCell(attributes)); | |
275 | return; | |
276 | } | |
277 | ||
278 | // tables | |
279 |
1
1. handleStartingTags : negated conditional → SURVIVED |
if (ElementTags.TABLE.equals(name)) { |
280 | Table table = ElementFactory.getTable(attributes); | |
281 | float[] widths = table.getProportionalWidths(); | |
282 |
2
1. handleStartingTags : negated conditional → SURVIVED 2. handleStartingTags : changed conditional boundary → KILLED |
for (int i = 0; i < widths.length; i++) { |
283 |
1
1. handleStartingTags : negated conditional → SURVIVED |
if (widths[i] == 0) { |
284 |
1
1. handleStartingTags : Replaced float division with multiplication → NO_COVERAGE |
widths[i] = 100.0f / widths.length; |
285 | } | |
286 | } | |
287 | try { | |
288 |
1
1. handleStartingTags : removed call to com/lowagie/text/Table::setWidths → SURVIVED |
table.setWidths(widths); |
289 | } catch (BadElementException bee) { | |
290 | // this shouldn't happen | |
291 | throw new ExceptionConverter(bee); | |
292 | } | |
293 | stack.push(table); | |
294 | return; | |
295 | } | |
296 | ||
297 | // sections | |
298 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.SECTION.equals(name)) { |
299 | Element previous = stack.pop(); | |
300 | Section section; | |
301 | try { | |
302 | section = ElementFactory.getSection((Section) previous, attributes); | |
303 | } catch (ClassCastException cce) { | |
304 | throw new ExceptionConverter(cce); | |
305 | } | |
306 | stack.push(previous); | |
307 | stack.push(section); | |
308 | return; | |
309 | } | |
310 | ||
311 | // chapters | |
312 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.CHAPTER.equals(name)) { |
313 | stack.push(ElementFactory.getChapter(attributes)); | |
314 | return; | |
315 | } | |
316 | ||
317 | // images | |
318 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.IMAGE.equals(name)) { |
319 | try { | |
320 | Image img = ElementFactory.getImage(attributes); | |
321 | try { | |
322 |
1
1. handleStartingTags : removed call to com/lowagie/text/xml/SAXiTextHandler::addImage → NO_COVERAGE |
addImage(img); |
323 | return; | |
324 | } catch (EmptyStackException ese) { | |
325 | // if there is no element on the stack, the Image is added | |
326 | // to the document | |
327 | try { | |
328 | document.add(img); | |
329 | } catch (DocumentException de) { | |
330 | throw new ExceptionConverter(de); | |
331 | } | |
332 | return; | |
333 | } | |
334 | } catch (Exception e) { | |
335 | throw new ExceptionConverter(e); | |
336 | } | |
337 | } | |
338 | ||
339 | // annotations | |
340 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.ANNOTATION.equals(name)) { |
341 | Annotation annotation = ElementFactory.getAnnotation(attributes); | |
342 | TextElementArray current; | |
343 | try { | |
344 | try { | |
345 | current = (TextElementArray) stack.pop(); | |
346 | try { | |
347 | current.add(annotation); | |
348 | } catch (Exception e) { | |
349 | document.add(annotation); | |
350 | } | |
351 | stack.push(current); | |
352 | } catch (EmptyStackException ese) { | |
353 | document.add(annotation); | |
354 | } | |
355 | return; | |
356 | } catch (DocumentException de) { | |
357 | throw new ExceptionConverter(de); | |
358 | } | |
359 | } | |
360 | ||
361 | // newlines | |
362 |
1
1. handleStartingTags : negated conditional → KILLED |
if (isNewline(name)) { |
363 | TextElementArray current; | |
364 | try { | |
365 | current = (TextElementArray) stack.pop(); | |
366 | current.add(Chunk.NEWLINE); | |
367 | stack.push(current); | |
368 | } catch (EmptyStackException ese) { | |
369 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (currentChunk == null) { |
370 | try { | |
371 | document.add(Chunk.NEWLINE); | |
372 | } catch (DocumentException de) { | |
373 | throw new ExceptionConverter(de); | |
374 | } | |
375 | } else { | |
376 | currentChunk.append("\n"); | |
377 | } | |
378 | } | |
379 | return; | |
380 | } | |
381 | ||
382 | // newpage | |
383 |
1
1. handleStartingTags : negated conditional → KILLED |
if (isNewpage(name)) { |
384 | TextElementArray current; | |
385 | try { | |
386 | current = (TextElementArray) stack.pop(); | |
387 | Chunk newPage = new Chunk(""); | |
388 | newPage.setNewPage(); | |
389 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (bf != null) { |
390 |
1
1. handleStartingTags : removed call to com/lowagie/text/Chunk::setFont → NO_COVERAGE |
newPage.setFont(new Font(this.bf)); |
391 | } | |
392 | current.add(newPage); | |
393 | stack.push(current); | |
394 | } catch (EmptyStackException ese) { | |
395 | document.newPage(); | |
396 | } | |
397 | return; | |
398 | } | |
399 | ||
400 |
1
1. handleStartingTags : negated conditional → KILLED |
if (ElementTags.HORIZONTALRULE.equals(name)) { |
401 | TextElementArray current; | |
402 | LineSeparator hr = new LineSeparator(1.0f, 100.0f, null, Element.ALIGN_CENTER, 0); | |
403 | try { | |
404 | current = (TextElementArray) stack.pop(); | |
405 | current.add(hr); | |
406 | stack.push(current); | |
407 | } catch (EmptyStackException ese) { | |
408 | try { | |
409 | document.add(hr); | |
410 | } catch (DocumentException de) { | |
411 | throw new ExceptionConverter(de); | |
412 | } | |
413 | } | |
414 | return; | |
415 | } | |
416 | ||
417 | // documentroot | |
418 |
1
1. handleStartingTags : negated conditional → SURVIVED |
if (isDocumentRoot(name)) { |
419 | String key; | |
420 | String value; | |
421 | // pagesize and orientation specific code suggested by Samuel Gabriel | |
422 | // Updated by Ricardo Coutinho. Only use if set in html! | |
423 | Rectangle pageSize = null; | |
424 | String orientation = null; | |
425 | for (Object o : attributes.keySet()) { | |
426 | key = (String) o; | |
427 | value = attributes.getProperty(key); | |
428 | try { | |
429 | // margin specific code suggested by Reza Nasiri | |
430 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (ElementTags.LEFT.equalsIgnoreCase(key)) |
431 | leftMargin = Float.parseFloat(value + "f"); | |
432 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (ElementTags.RIGHT.equalsIgnoreCase(key)) |
433 | rightMargin = Float.parseFloat(value + "f"); | |
434 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (ElementTags.TOP.equalsIgnoreCase(key)) |
435 | topMargin = Float.parseFloat(value + "f"); | |
436 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (ElementTags.BOTTOM.equalsIgnoreCase(key)) |
437 | bottomMargin = Float.parseFloat(value + "f"); | |
438 | } catch (Exception ex) { | |
439 | throw new ExceptionConverter(ex); | |
440 | } | |
441 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (ElementTags.PAGE_SIZE.equals(key)) { |
442 | try { | |
443 | String pageSizeName = value; | |
444 | Field pageSizeField = PageSize.class.getField(pageSizeName); | |
445 | pageSize = (Rectangle) pageSizeField.get(null); | |
446 | } catch (Exception ex) { | |
447 | throw new ExceptionConverter(ex); | |
448 | } | |
449 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
} else if (ElementTags.ORIENTATION.equals(key)) { |
450 | try { | |
451 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if ("landscape".equals(value)) { |
452 | orientation = "landscape"; | |
453 | } | |
454 | } catch (Exception ex) { | |
455 | throw new ExceptionConverter(ex); | |
456 | } | |
457 | } else { | |
458 | try { | |
459 | document.add(new Meta(key, value)); | |
460 | } catch (DocumentException de) { | |
461 | throw new ExceptionConverter(de); | |
462 | } | |
463 | } | |
464 | } | |
465 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (pageSize != null) { |
466 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if ("landscape".equals(orientation)) { |
467 | pageSize = pageSize.rotate(); | |
468 | } | |
469 | document.setPageSize(pageSize); | |
470 | } | |
471 | document.setMargins(leftMargin, rightMargin, topMargin, bottomMargin); | |
472 | ||
473 |
1
1. handleStartingTags : negated conditional → NO_COVERAGE |
if (controlOpenClose) |
474 |
1
1. handleStartingTags : removed call to com/lowagie/text/DocListener::open → NO_COVERAGE |
document.open(); |
475 | } | |
476 | ||
477 | } | |
478 | ||
479 | /** | |
480 | * This method gets called when ignorable white space encountered. | |
481 | * | |
482 | * @param ch an array of characters | |
483 | * @param start the start position in the array | |
484 | * @param length the number of characters to read from the array | |
485 | */ | |
486 | ||
487 | public void ignorableWhitespace(char[] ch, int start, int length) { | |
488 | characters(ch, start, length); | |
489 | } | |
490 | ||
491 | /** | |
492 | * This method gets called when characters are encountered. | |
493 | * | |
494 | * @param ch an array of characters | |
495 | * @param start the start position in the array | |
496 | * @param length the number of characters to read from the array | |
497 | */ | |
498 | ||
499 | public void characters(char[] ch, int start, int length) { | |
500 |
1
1. characters : negated conditional → SURVIVED |
if (ignore) { |
501 | return; | |
502 | } | |
503 | ||
504 | String content = new String(ch, start, length); | |
505 |
3
1. characters : changed conditional boundary → SURVIVED 2. characters : negated conditional → SURVIVED 3. characters : negated conditional → SURVIVED |
if (content.trim().isEmpty() && content.indexOf(' ') < 0) { |
506 | return; | |
507 | } | |
508 | ||
509 | StringBuilder buf = new StringBuilder(); | |
510 | int len = content.length(); | |
511 | char character; | |
512 | boolean newline = false; | |
513 |
3
1. characters : negated conditional → SURVIVED 2. characters : changed conditional boundary → KILLED 3. characters : Changed increment from 1 to -1 → KILLED |
for (int i = 0; i < len; i++) { |
514 | switch (character = content.charAt(i)) { | |
515 | case ' ': | |
516 |
1
1. characters : negated conditional → SURVIVED |
if (!newline) { |
517 | buf.append(character); | |
518 | } | |
519 | break; | |
520 | case '\n': | |
521 |
2
1. characters : changed conditional boundary → NO_COVERAGE 2. characters : negated conditional → NO_COVERAGE |
if (i > 0) { |
522 | newline = true; | |
523 | buf.append(' '); | |
524 | } | |
525 | break; | |
526 | case '\r': | |
527 | break; | |
528 | case '\t': | |
529 | break; | |
530 | default: | |
531 | newline = false; | |
532 | buf.append(character); | |
533 | } | |
534 | } | |
535 |
1
1. characters : negated conditional → KILLED |
if (currentChunk == null) { |
536 |
1
1. characters : negated conditional → SURVIVED |
if (bf == null) { |
537 | currentChunk = new Chunk(buf.toString()); | |
538 | } else { | |
539 | currentChunk = new Chunk(buf.toString(), new Font(this.bf)); | |
540 | } | |
541 | } else { | |
542 | currentChunk.append(buf.toString()); | |
543 | } | |
544 | } | |
545 | ||
546 | /** | |
547 | * Sets the font that has to be used. | |
548 | * | |
549 | * @param bf | |
550 | */ | |
551 | public void setBaseFont(BaseFont bf) { | |
552 | this.bf = bf; | |
553 | } | |
554 | ||
555 | /** | |
556 | * This method gets called when an end tag is encountered. | |
557 | * | |
558 | * @param uri the Uniform Resource Identifier | |
559 | * @param lname the local name (without prefix), or the empty string if | |
560 | * Namespace processing is not being performed. | |
561 | * @param name the name of the tag that ends | |
562 | */ | |
563 | ||
564 | public void endElement(String uri, String lname, String name) { | |
565 | handleEndingTags(name); | |
566 | } | |
567 | ||
568 | /** | |
569 | * This method deals with the starting tags. | |
570 | * | |
571 | * @param name the name of the tag | |
572 | */ | |
573 | ||
574 | public void handleEndingTags(String name) { | |
575 | ||
576 | // System.err.println("Stop: " + name); | |
577 | ||
578 |
1
1. handleEndingTags : negated conditional → SURVIVED |
if (ElementTags.IGNORE.equals(name)) { |
579 | ignore = false; | |
580 | return; | |
581 | } | |
582 |
1
1. handleEndingTags : negated conditional → SURVIVED |
if (ignore) |
583 | return; | |
584 | // tags that don't have any content | |
585 |
3
1. handleEndingTags : negated conditional → SURVIVED 2. handleEndingTags : negated conditional → SURVIVED 3. handleEndingTags : negated conditional → SURVIVED |
if (isNewpage(name) || ElementTags.ANNOTATION.equals(name) || ElementTags.IMAGE.equals(name) |
586 |
1
1. handleEndingTags : negated conditional → SURVIVED |
|| isNewline(name)) { |
587 | return; | |
588 | } | |
589 | ||
590 | try { | |
591 | // titles of sections and chapters | |
592 |
1
1. handleEndingTags : negated conditional → KILLED |
if (ElementTags.TITLE.equals(name)) { |
593 | Paragraph current = (Paragraph) stack.pop(); | |
594 |
1
1. handleEndingTags : negated conditional → NO_COVERAGE |
if (currentChunk != null) { |
595 | current.add(currentChunk); | |
596 | currentChunk = null; | |
597 | } | |
598 | Section previous = (Section) stack.pop(); | |
599 |
1
1. handleEndingTags : removed call to com/lowagie/text/Section::setTitle → NO_COVERAGE |
previous.setTitle(current); |
600 | stack.push(previous); | |
601 | return; | |
602 | } | |
603 | ||
604 | // all other endtags | |
605 |
1
1. handleEndingTags : negated conditional → SURVIVED |
if (currentChunk != null) { |
606 | TextElementArray current; | |
607 | try { | |
608 | current = (TextElementArray) stack.pop(); | |
609 | } catch (EmptyStackException ese) { | |
610 | current = new Paragraph(); | |
611 | } | |
612 | current.add(currentChunk); | |
613 | stack.push(current); | |
614 | currentChunk = null; | |
615 | } | |
616 | ||
617 | // chunks | |
618 |
1
1. handleEndingTags : negated conditional → SURVIVED |
if (ElementTags.CHUNK.equals(name)) { |
619 | return; | |
620 | } | |
621 | ||
622 | // phrases, anchors, lists, tables | |
623 |
3
1. handleEndingTags : negated conditional → KILLED 2. handleEndingTags : negated conditional → KILLED 3. handleEndingTags : negated conditional → KILLED |
if (ElementTags.PHRASE.equals(name) || ElementTags.ANCHOR.equals(name) || ElementTags.LIST.equals(name) |
624 |
1
1. handleEndingTags : negated conditional → KILLED |
|| ElementTags.PARAGRAPH.equals(name)) { |
625 | Element current = stack.pop(); | |
626 | try { | |
627 | TextElementArray previous = (TextElementArray) stack.pop(); | |
628 | previous.add(current); | |
629 | stack.push(previous); | |
630 | } catch (EmptyStackException ese) { | |
631 | document.add(current); | |
632 | } | |
633 | return; | |
634 | } | |
635 | ||
636 | // listitems | |
637 |
1
1. handleEndingTags : negated conditional → KILLED |
if (ElementTags.LISTITEM.equals(name)) { |
638 | ListItem listItem = (ListItem) stack.pop(); | |
639 | List list = (List) stack.pop(); | |
640 | list.add(listItem); | |
641 | stack.push(list); | |
642 | } | |
643 | ||
644 | // tables | |
645 |
1
1. handleEndingTags : negated conditional → KILLED |
if (ElementTags.TABLE.equals(name)) { |
646 | Table table = (Table) stack.pop(); | |
647 | try { | |
648 | TextElementArray previous = (TextElementArray) stack.pop(); | |
649 | previous.add(table); | |
650 | stack.push(previous); | |
651 | } catch (EmptyStackException ese) { | |
652 | document.add(table); | |
653 | } | |
654 | return; | |
655 | } | |
656 | ||
657 | // rows | |
658 |
1
1. handleEndingTags : negated conditional → SURVIVED |
if (ElementTags.ROW.equals(name)) { |
659 | java.util.List<Cell> cells = new ArrayList<>(); | |
660 | int columns = 0; | |
661 | Table table; | |
662 | Cell cell; | |
663 | while (true) { | |
664 | Element element = stack.pop(); | |
665 |
1
1. handleEndingTags : negated conditional → KILLED |
if (element.type() == Element.CELL) { |
666 | cell = (Cell) element; | |
667 |
1
1. handleEndingTags : Replaced integer addition with subtraction → KILLED |
columns += cell.getColspan(); |
668 | cells.add(cell); | |
669 | } else { | |
670 | table = (Table) element; | |
671 | break; | |
672 | } | |
673 | } | |
674 |
2
1. handleEndingTags : changed conditional boundary → SURVIVED 2. handleEndingTags : negated conditional → SURVIVED |
if (table.getColumns() < columns) { |
675 |
2
1. handleEndingTags : Replaced integer subtraction with addition → NO_COVERAGE 2. handleEndingTags : removed call to com/lowagie/text/Table::addColumns → NO_COVERAGE |
table.addColumns(columns - table.getColumns()); |
676 | } | |
677 |
1
1. handleEndingTags : removed call to java/util/Collections::reverse → SURVIVED |
Collections.reverse(cells); |
678 | String width; | |
679 | float[] cellWidths = new float[columns]; | |
680 | boolean[] cellNulls = new boolean[columns]; | |
681 |
3
1. handleEndingTags : negated conditional → SURVIVED 2. handleEndingTags : changed conditional boundary → KILLED 3. handleEndingTags : Changed increment from 1 to -1 → KILLED |
for (int i = 0; i < columns; i++) { |
682 | cellWidths[i] = 0; | |
683 | cellNulls[i] = true; | |
684 | } | |
685 | float total = 0.0f; | |
686 | int j = 0; | |
687 | for (Cell value : cells) { | |
688 | cell = value; | |
689 | width = cell.getWidthAsString(); | |
690 |
1
1. handleEndingTags : negated conditional → SURVIVED |
if (cell.getWidth() == 0) { |
691 |
2
1. handleEndingTags : negated conditional → SURVIVED 2. handleEndingTags : negated conditional → SURVIVED |
if (cell.getColspan() == 1 && cellWidths[j] == 0) { |
692 | try { | |
693 |
1
1. handleEndingTags : Replaced float division with multiplication → SURVIVED |
cellWidths[j] = 100.0f / columns; |
694 |
1
1. handleEndingTags : Replaced float addition with subtraction → SURVIVED |
total += cellWidths[j]; |
695 | } catch (Exception e) { | |
696 | // empty on purpose | |
697 | } | |
698 |
1
1. handleEndingTags : negated conditional → NO_COVERAGE |
} else if (cell.getColspan() == 1) { |
699 | cellNulls[j] = false; | |
700 | } | |
701 |
2
1. handleEndingTags : negated conditional → NO_COVERAGE 2. handleEndingTags : negated conditional → NO_COVERAGE |
} else if (cell.getColspan() == 1 && width.endsWith("%")) { |
702 | try { | |
703 |
1
1. handleEndingTags : Replaced integer subtraction with addition → NO_COVERAGE |
cellWidths[j] = Float.parseFloat(width.substring(0, width.length() - 1) + "f"); |
704 |
1
1. handleEndingTags : Replaced float addition with subtraction → NO_COVERAGE |
total += cellWidths[j]; |
705 | cellNulls[j] = false; | |
706 | } catch (Exception e) { | |
707 | // empty on purpose | |
708 | } | |
709 | } | |
710 |
1
1. handleEndingTags : Replaced integer addition with subtraction → SURVIVED |
j += cell.getColspan(); |
711 |
1
1. handleEndingTags : removed call to com/lowagie/text/Table::addCell → SURVIVED |
table.addCell(cell); |
712 | } | |
713 | float[] widths = table.getProportionalWidths(); | |
714 |
1
1. handleEndingTags : negated conditional → SURVIVED |
if (widths.length == columns) { |
715 | float left = 0.0f; | |
716 |
3
1. handleEndingTags : negated conditional → SURVIVED 2. handleEndingTags : changed conditional boundary → KILLED 3. handleEndingTags : Changed increment from 1 to -1 → KILLED |
for (int i = 0; i < columns; i++) { |
717 |
2
1. handleEndingTags : negated conditional → SURVIVED 2. handleEndingTags : negated conditional → SURVIVED |
if (cellNulls[i] && widths[i] != 0) { |
718 |
1
1. handleEndingTags : Replaced float addition with subtraction → SURVIVED |
left += widths[i]; |
719 | cellWidths[i] = widths[i]; | |
720 | } | |
721 | } | |
722 |
2
1. handleEndingTags : changed conditional boundary → SURVIVED 2. handleEndingTags : negated conditional → SURVIVED |
if (100.0 >= total) { |
723 |
2
1. handleEndingTags : negated conditional → SURVIVED 2. handleEndingTags : changed conditional boundary → KILLED |
for (int i = 0; i < widths.length; i++) { |
724 |
2
1. handleEndingTags : negated conditional → SURVIVED 2. handleEndingTags : negated conditional → SURVIVED |
if (cellWidths[i] == 0 && widths[i] != 0) { |
725 |
3
1. handleEndingTags : Replaced float division with multiplication → NO_COVERAGE 2. handleEndingTags : Replaced float subtraction with addition → NO_COVERAGE 3. handleEndingTags : Replaced float multiplication with division → NO_COVERAGE |
cellWidths[i] = (widths[i] / left) * (100.0f - total); |
726 | } | |
727 | } | |
728 | } | |
729 |
1
1. handleEndingTags : removed call to com/lowagie/text/Table::setWidths → SURVIVED |
table.setWidths(cellWidths); |
730 | } | |
731 | stack.push(table); | |
732 | } | |
733 | ||
734 | // cells | |
735 |
1
1. handleEndingTags : negated conditional → SURVIVED |
if (ElementTags.CELL.equals(name)) { |
736 | return; | |
737 | } | |
738 | ||
739 | // sections | |
740 |
1
1. handleEndingTags : negated conditional → KILLED |
if (ElementTags.SECTION.equals(name)) { |
741 | stack.pop(); | |
742 | return; | |
743 | } | |
744 | ||
745 | // chapters | |
746 |
1
1. handleEndingTags : negated conditional → KILLED |
if (ElementTags.CHAPTER.equals(name)) { |
747 | document.add(stack.pop()); | |
748 | return; | |
749 | } | |
750 | ||
751 | // the documentroot | |
752 |
1
1. handleEndingTags : negated conditional → KILLED |
if (isDocumentRoot(name)) { |
753 | try { | |
754 | while (true) { | |
755 | Element element = stack.pop(); | |
756 | try { | |
757 | TextElementArray previous = (TextElementArray) stack.pop(); | |
758 | previous.add(element); | |
759 | stack.push(previous); | |
760 | } catch (EmptyStackException es) { | |
761 | document.add(element); | |
762 | } | |
763 | } | |
764 | } catch (EmptyStackException ese) { | |
765 | // empty on purpose | |
766 | } | |
767 |
1
1. handleEndingTags : negated conditional → NO_COVERAGE |
if (controlOpenClose) { |
768 |
1
1. handleEndingTags : removed call to com/lowagie/text/DocListener::close → NO_COVERAGE |
document.close(); |
769 | } | |
770 | } | |
771 | } catch (DocumentException de) { | |
772 | throw new ExceptionConverter(de); | |
773 | } | |
774 | } | |
775 | ||
776 | private boolean isNotBlank(String text) { | |
777 |
3
1. isNotBlank : negated conditional → SURVIVED 2. isNotBlank : negated conditional → KILLED 3. isNotBlank : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return text != null && !text.trim().isEmpty(); |
778 | } | |
779 | ||
780 | protected void addImage(Image img) throws EmptyStackException { | |
781 | // if there is an element on the stack... | |
782 | Element current = stack.pop(); | |
783 | // ...and it's a Chapter or a Section, the Image can be | |
784 | // added directly | |
785 |
2
1. addImage : negated conditional → NO_COVERAGE 2. addImage : negated conditional → NO_COVERAGE |
if (current instanceof Section || current instanceof Cell) { |
786 | ((TextElementArray) current).add(img); | |
787 | stack.push(current); | |
788 | } | |
789 | // ...if not, we need to to a lot of stuff | |
790 | else { | |
791 | Stack<Element> newStack = new Stack<>(); | |
792 |
2
1. addImage : negated conditional → NO_COVERAGE 2. addImage : negated conditional → NO_COVERAGE |
while (!(current instanceof Section || current instanceof Cell)) { |
793 | newStack.push(current); | |
794 |
1
1. addImage : negated conditional → NO_COVERAGE |
if (current instanceof Anchor) { |
795 |
1
1. addImage : removed call to com/lowagie/text/Image::setAnnotation → NO_COVERAGE |
img.setAnnotation(new Annotation(0, 0, 0, |
796 | 0, ((Anchor) current).getReference())); | |
797 | } | |
798 | current = stack.pop(); | |
799 | } | |
800 | ((TextElementArray) current).add(img); | |
801 | stack.push(current); | |
802 |
1
1. addImage : negated conditional → NO_COVERAGE |
while (!newStack.empty()) { |
803 | stack.push(newStack.pop()); | |
804 | } | |
805 | } | |
806 | } | |
807 | ||
808 | /** | |
809 | * Checks if a certain tag corresponds with the newpage-tag. | |
810 | * | |
811 | * @param tag a presumed tagname | |
812 | * @return <CODE>true</CODE> or <CODE>false</CODE> | |
813 | */ | |
814 | ||
815 | private boolean isNewpage(String tag) { | |
816 |
1
1. isNewpage : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return ElementTags.NEWPAGE.equals(tag); |
817 | } | |
818 | ||
819 | /** | |
820 | * Checks if a certain tag corresponds with the newpage-tag. | |
821 | * | |
822 | * @param tag a presumed tagname | |
823 | * @return <CODE>true</CODE> or <CODE>false</CODE> | |
824 | */ | |
825 | ||
826 | private boolean isNewline(String tag) { | |
827 |
1
1. isNewline : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return ElementTags.NEWLINE.equals(tag); |
828 | } | |
829 | ||
830 | /** | |
831 | * Checks if a certain tag corresponds with the roottag. | |
832 | * | |
833 | * @param tag a presumed tagname | |
834 | * @return <CODE>true</CODE> if <VAR>tag </VAR> equals <CODE>itext | |
835 | * </CODE>,<CODE>false</CODE> otherwise. | |
836 | */ | |
837 | ||
838 | protected boolean isDocumentRoot(String tag) { | |
839 |
1
1. isDocumentRoot : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return ElementTags.ITEXT.equals(tag); |
840 | } | |
841 | } | |
Mutations | ||
182 |
1.1 |
|
183 |
1.1 2.2 |
|
188 |
1.1 |
|
199 |
1.1 2.2 |
|
205 |
1.1 2.2 |
|
210 |
1.1 |
|
222 |
1.1 |
|
224 |
1.1 |
|
225 |
1.1 |
|
231 |
1.1 |
|
233 |
1.1 |
|
234 |
1.1 |
|
243 |
1.1 |
|
249 |
1.1 |
|
255 |
1.1 2.2 |
|
261 |
1.1 |
|
267 |
1.1 |
|
273 |
1.1 |
|
279 |
1.1 |
|
282 |
1.1 2.2 |
|
283 |
1.1 |
|
284 |
1.1 |
|
288 |
1.1 |
|
298 |
1.1 |
|
312 |
1.1 |
|
318 |
1.1 |
|
322 |
1.1 |
|
340 |
1.1 |
|
362 |
1.1 |
|
369 |
1.1 |
|
383 |
1.1 |
|
389 |
1.1 |
|
390 |
1.1 |
|
400 |
1.1 |
|
418 |
1.1 |
|
430 |
1.1 |
|
432 |
1.1 |
|
434 |
1.1 |
|
436 |
1.1 |
|
441 |
1.1 |
|
449 |
1.1 |
|
451 |
1.1 |
|
465 |
1.1 |
|
466 |
1.1 |
|
473 |
1.1 |
|
474 |
1.1 |
|
500 |
1.1 |
|
505 |
1.1 2.2 3.3 |
|
513 |
1.1 2.2 3.3 |
|
516 |
1.1 |
|
521 |
1.1 2.2 |
|
535 |
1.1 |
|
536 |
1.1 |
|
578 |
1.1 |
|
582 |
1.1 |
|
585 |
1.1 2.2 3.3 |
|
586 |
1.1 |
|
592 |
1.1 |
|
594 |
1.1 |
|
599 |
1.1 |
|
605 |
1.1 |
|
618 |
1.1 |
|
623 |
1.1 2.2 3.3 |
|
624 |
1.1 |
|
637 |
1.1 |
|
645 |
1.1 |
|
658 |
1.1 |
|
665 |
1.1 |
|
667 |
1.1 |
|
674 |
1.1 2.2 |
|
675 |
1.1 2.2 |
|
677 |
1.1 |
|
681 |
1.1 2.2 3.3 |
|
690 |
1.1 |
|
691 |
1.1 2.2 |
|
693 |
1.1 |
|
694 |
1.1 |
|
698 |
1.1 |
|
701 |
1.1 2.2 |
|
703 |
1.1 |
|
704 |
1.1 |
|
710 |
1.1 |
|
711 |
1.1 |
|
714 |
1.1 |
|
716 |
1.1 2.2 3.3 |
|
717 |
1.1 2.2 |
|
718 |
1.1 |
|
722 |
1.1 2.2 |
|
723 |
1.1 2.2 |
|
724 |
1.1 2.2 |
|
725 |
1.1 2.2 3.3 |
|
729 |
1.1 |
|
735 |
1.1 |
|
740 |
1.1 |
|
746 |
1.1 |
|
752 |
1.1 |
|
767 |
1.1 |
|
768 |
1.1 |
|
777 |
1.1 2.2 3.3 |
|
785 |
1.1 2.2 |
|
792 |
1.1 2.2 |
|
794 |
1.1 |
|
795 |
1.1 |
|
802 |
1.1 |
|
816 |
1.1 |
|
827 |
1.1 |
|
839 |
1.1 |