1 /*
2  * $Id: PdfPRow.java 3999 2009-06-30 11:52:55Z blowagie $
3  *
4  * Copyright 2001, 2002 Paulo Soares
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.pdf;
51
52 import java.awt.Color;
53
54 import com.lowagie.text.DocumentException;
55 import com.lowagie.text.Element;
56 import com.lowagie.text.ExceptionConverter;
57 import com.lowagie.text.Image;
58 import com.lowagie.text.Rectangle;
59
60 /**
61  * A row in a PdfPTable.
62  * 
63  * @author Paulo Soares (psoares@consiste.pt)
64  */

65 public class PdfPRow {
66
67     /** the bottom limit (bottom right y) */
68     public static final float BOTTOM_LIMIT = -(1 << 30);
69     /**
70      * the right limit
71      * @since    2.1.5
72      */

73     public static final float RIGHT_LIMIT = 20000;
74
75     protected PdfPCell cells[];
76
77     protected float widths[];
78     
79     /**
80      * extra heights that needs to be added to a cell because of rowspans.
81      * @since    2.1.6
82      */

83     protected float extraHeights[];
84
85     protected float maxHeight = 0;
86     
87     protected boolean calculated = false;
88     
89     private int[] canvasesPos;
90     
91     /**
92      * Constructs a new PdfPRow with the cells in the array that was passed
93      * as a parameter.
94      * 
95      * @param cells
96      */

97     public PdfPRow(PdfPCell cells[]) {
98         this.cells = cells;
99         widths = new float[cells.length];
100         initExtraHeights();
101     }
102
103     /**
104      * Makes a copy of an existing row.
105      * 
106      * @param row
107      */

108     public PdfPRow(PdfPRow row) {
109         maxHeight = row.maxHeight;
110         calculated = row.calculated;
111         cells = new PdfPCell[row.cells.length];
112         for (int k = 0; k < cells.length; ++k) {
113             if (row.cells[k] != null)
114                 cells[k] = new PdfPCell(row.cells[k]);
115         }
116         widths = new float[cells.length];
117         System.arraycopy(row.widths, 0, widths, 0, cells.length);
118         initExtraHeights();
119     }
120
121     /**
122      * Sets the widths of the columns in the row.
123      * 
124      * @param widths
125      * @return true if everything went right
126      */

127     public boolean setWidths(float widths[]) {
128         if (widths.length != cells.length)
129             return false;
130         System.arraycopy(widths, 0, this.widths, 0, cells.length);
131         float total = 0;
132         calculated = false;
133         for (int k = 0; k < widths.length; ++k) {
134             PdfPCell cell = cells[k];
135             
136             if (cell == null) {
137                 total += widths[k];
138                 continue;
139             }
140             
141             cell.setLeft(total);
142             int last = k + cell.getColspan();
143             for (; k < last; ++k)
144                 total += widths[k];
145             --k;
146             cell.setRight(total);
147             cell.setTop(0);
148         }
149         return true;
150     }
151
152     /**
153      * Initializes the extra heights array.
154      * @since    2.1.6
155      */

156     public void initExtraHeights() {
157         extraHeights = new float[cells.length];
158         for (int i = 0; i < extraHeights.length; i++) {
159             extraHeights[i] = 0;
160         }
161     }
162     
163     /**
164      * Sets an extra height for a cell.
165      * @param    cell    the index of the cell that needs an extra height
166      * @param    height    the extra height
167      * @since    2.1.6
168      */

169     public void setExtraHeight(int cell, float height) {
170         if (cell < 0 || cell >= cells.length)
171             return;
172         extraHeights[cell] = height;
173     }
174     
175     /**
176      * Calculates the heights of each cell in the row.
177      * 
178      * @return the maximum height of the row.
179      */

180     public float calculateHeights() {
181         maxHeight = 0;
182         for (int k = 0; k < cells.length; ++k) {
183             PdfPCell cell = cells[k];
184             float height = 0;
185             if (cell == null) {
186                 continue;
187             }
188             else {
189                 height = cell.getMaxHeight();
190                 if ((height > maxHeight) && (cell.getRowspan() == 1))
191                     maxHeight = height;
192             }
193         }
194         calculated = true;
195         return maxHeight;
196     }
197
198     /**
199      * Writes the border and background of one cell in the row.
200      * 
201      * @param xPos The x-coordinate where the table starts on the canvas
202      * @param yPos The y-coordinate where the table starts on the canvas
203      * @param currentMaxHeight The height of the cell to be drawn.
204      * @param cell
205      * @param canvases
206      * @since    2.1.6    extra parameter currentMaxHeight
207      */

208     public void writeBorderAndBackground(float xPos, float yPos, float currentMaxHeight, PdfPCell cell, PdfContentByte[] canvases) {
209         Color background = cell.getBackgroundColor();
210         if (background != null || cell.hasBorders()) {
211             // Add xPos resp. yPos to the cell's coordinates for absolute coordinates
212             float right = cell.getRight() + xPos;
213             float top = cell.getTop() + yPos;
214             float left = cell.getLeft() + xPos;
215             float bottom = top - currentMaxHeight;
216             
217             if (background != null) {
218                 PdfContentByte backgr = canvases[PdfPTable.BACKGROUNDCANVAS];
219                 backgr.setColorFill(background);
220                 backgr.rectangle(left, bottom, right - left, top - bottom);
221                 backgr.fill();
222             }
223             if (cell.hasBorders()) {
224                 Rectangle newRect = new Rectangle(left, bottom, right, top);
225                 // Clone non-position parameters except for the background color
226                 newRect.cloneNonPositionParameters(cell);
227                 newRect.setBackgroundColor(null);
228                 // Write the borders on the line canvas
229                 PdfContentByte lineCanvas = canvases[PdfPTable.LINECANVAS];
230                 lineCanvas.rectangle(newRect);
231             }
232         }
233     }
234
235     /**
236      * @since    2.1.6 private is now protected
237      */

238     protected void saveAndRotateCanvases(PdfContentByte[] canvases, float a, float b, float c, float d, float e, float f) {
239         int last = PdfPTable.TEXTCANVAS + 1;
240         if (canvasesPos == null)
241             canvasesPos = new int[last * 2];
242         for (int k = 0; k < last; ++k) {
243             ByteBuffer bb = canvases[k].getInternalBuffer();
244             canvasesPos[k * 2] = bb.size();
245             canvases[k].saveState();
246             canvases[k].concatCTM(a, b, c, d, e, f);
247             canvasesPos[k * 2 + 1] = bb.size();
248         }
249     }
250
251     /**
252      * @since    2.1.6 private is now protected
253      */

254     protected void restoreCanvases(PdfContentByte[] canvases) {
255         int last = PdfPTable.TEXTCANVAS + 1;
256         for (int k = 0; k < last; ++k) {
257             ByteBuffer bb = canvases[k].getInternalBuffer();
258             int p1 = bb.size();
259             canvases[k].restoreState();
260             if (p1 == canvasesPos[k * 2 + 1])
261                 bb.setSize(canvasesPos[k * 2]);
262         }
263     }
264
265     /**
266      * @since    3.0.0 protected is now public static
267      */

268     public static float setColumn(ColumnText ct, float left, float bottom, float right, float top) {
269         if (left > right)
270             right = left;
271         if (bottom > top)
272             top = bottom;
273         ct.setSimpleColumn(left, bottom, right, top);
274         return top;
275     }
276     
277     /**
278      * Writes a number of cells (not necessarily all cells).
279      * 
280      * @param    colStart The first column to be written.
281      * Remember that the column index starts with 0.
282      * @param    colEnd The last column to be written.
283      * Remember that the column index starts with 0.
284      * If -1, all the columns to the end are written.
285      * @param    xPos The x-coordinate where the table starts on the canvas
286      * @param    yPos The y-coordinate where the table starts on the canvas
287      */

288     public void writeCells(int colStart, int colEnd, float xPos, float yPos, PdfContentByte[] canvases) {
289         if (!calculated)
290             calculateHeights();
291         if (colEnd < 0)
292             colEnd = cells.length;
293         else
294             colEnd = Math.min(colEnd, cells.length);
295         if (colStart < 0)
296             colStart = 0;
297         if (colStart >= colEnd)
298             return;
299         
300         int newStart;
301         for (newStart = colStart; newStart >= 0; --newStart) {
302             if (cells[newStart] != null)
303                 break;
304             if (newStart > 0)
305                 xPos -= widths[newStart - 1];
306         }
307         
308         if (newStart < 0)
309             newStart = 0;
310         if (cells[newStart] != null)
311             xPos -= cells[newStart].getLeft();
312         
313         for (int k = newStart; k < colEnd; ++k) {
314             PdfPCell cell = cells[k];
315             if (cell == null)
316                 continue;
317             float currentMaxHeight = maxHeight + extraHeights[k];
318             
319             writeBorderAndBackground(xPos, yPos, currentMaxHeight, cell, canvases);
320
321             Image img = cell.getImage();
322             
323             float tly = cell.getTop() + yPos - cell.getEffectivePaddingTop();
324             if (cell.getHeight() <= currentMaxHeight) {
325                 switch (cell.getVerticalAlignment()) {
326                 case Element.ALIGN_BOTTOM:
327                     tly = cell.getTop() + yPos - currentMaxHeight + cell.getHeight()
328                             - cell.getEffectivePaddingTop();
329                     break;
330                 case Element.ALIGN_MIDDLE:
331                     tly = cell.getTop() + yPos + (cell.getHeight() - currentMaxHeight) / 2
332                             - cell.getEffectivePaddingTop();
333                     break;
334                 default:
335                     break;
336                 }
337             }
338             if (img != null) {
339                 if (cell.getRotation() != 0) {
340                     img = Image.getInstance(img);
341                     img.setRotation(img.getImageRotation() + (float)(cell.getRotation() * Math.PI / 180.0));
342                 }
343                 boolean vf = false;
344                 if (cell.getHeight() > currentMaxHeight) {
345                     img.scalePercent(100);
346                     float scale = (currentMaxHeight - cell.getEffectivePaddingTop() - cell
347                             .getEffectivePaddingBottom())
348                             / img.getScaledHeight();
349                     img.scalePercent(scale * 100);
350                     vf = true;
351                 }
352                 float left = cell.getLeft() + xPos
353                         + cell.getEffectivePaddingLeft();
354                 if (vf) {
355                     switch (cell.getHorizontalAlignment()) {
356                     case Element.ALIGN_CENTER:
357                         left = xPos
358                                 + (cell.getLeft() + cell.getEffectivePaddingLeft()
359                                         + cell.getRight()
360                                         - cell.getEffectivePaddingRight() - img
361                                         .getScaledWidth()) / 2;
362                         break;
363                     case Element.ALIGN_RIGHT:
364                         left = xPos + cell.getRight()
365                                 - cell.getEffectivePaddingRight()
366                                 - img.getScaledWidth();
367                         break;
368                     default:
369                         break;
370                     }
371                     tly = cell.getTop() + yPos - cell.getEffectivePaddingTop();
372                 }
373                 img.setAbsolutePosition(left, tly - img.getScaledHeight());
374                 try {
375                     canvases[PdfPTable.TEXTCANVAS].addImage(img);
376                 } catch (DocumentException e) {
377                     throw new ExceptionConverter(e);
378                 }
379             } else {
380                 // rotation sponsored by Connection GmbH
381                 if (cell.getRotation() == 90 || cell.getRotation() == 270) {
382                     float netWidth = currentMaxHeight - cell.getEffectivePaddingTop() - cell.getEffectivePaddingBottom();
383                     float netHeight = cell.getWidth() - cell.getEffectivePaddingLeft() - cell.getEffectivePaddingRight();
384                     ColumnText ct = ColumnText.duplicate(cell.getColumn());
385                     ct.setCanvases(canvases);
386                     ct.setSimpleColumn(0, 0, netWidth + 0.001f, -netHeight);
387                     try {
388                         ct.go(true);
389                     } catch (DocumentException e) {
390                         throw new ExceptionConverter(e);
391                     }
392                     float calcHeight = -ct.getYLine();
393                     if (netWidth <= 0 || netHeight <= 0)
394                         calcHeight = 0;
395                     if (calcHeight > 0) {
396                         if (cell.isUseDescender())
397                             calcHeight -= ct.getDescender();
398                         ct = ColumnText.duplicate(cell.getColumn());
399                         ct.setCanvases(canvases);
400                         ct.setSimpleColumn(-0.003f, -0.001f, netWidth + 0.003f, calcHeight);
401                         float pivotX;
402                         float pivotY;
403                         if (cell.getRotation() == 90) {
404                             pivotY = cell.getTop() + yPos - currentMaxHeight + cell.getEffectivePaddingBottom();
405                             switch (cell.getVerticalAlignment()) {
406                             case Element.ALIGN_BOTTOM:
407                                 pivotX = cell.getLeft() + xPos + cell.getWidth() - cell.getEffectivePaddingRight();
408                                 break;
409                             case Element.ALIGN_MIDDLE:
410                                 pivotX = cell.getLeft() + xPos + (cell.getWidth() + cell.getEffectivePaddingLeft() - cell.getEffectivePaddingRight() + calcHeight) / 2;
411                                 break;
412                             default//top
413                                 pivotX = cell.getLeft() + xPos + cell.getEffectivePaddingLeft() + calcHeight;
414                                 break;
415                             }
416                             saveAndRotateCanvases(canvases, 0,1,-1,0,pivotX,pivotY);
417                         }
418                         else {
419                             pivotY = cell.getTop() + yPos - cell.getEffectivePaddingTop();
420                             switch (cell.getVerticalAlignment()) {
421                             case Element.ALIGN_BOTTOM:
422                                 pivotX = cell.getLeft() + xPos + cell.getEffectivePaddingLeft();
423                                 break;
424                             case Element.ALIGN_MIDDLE:
425                                 pivotX = cell.getLeft() + xPos + (cell.getWidth() + cell.getEffectivePaddingLeft() - cell.getEffectivePaddingRight() - calcHeight) / 2;
426                                 break;
427                             default//top
428                                 pivotX = cell.getLeft() + xPos + cell.getWidth() - cell.getEffectivePaddingRight() - calcHeight;
429                                 break;
430                             }
431                             saveAndRotateCanvases(canvases, 0,-1,1,0,pivotX,pivotY);
432                         }
433                         try {
434                             ct.go();
435                         } catch (DocumentException e) {
436                             throw new ExceptionConverter(e);
437                         } finally {
438                             restoreCanvases(canvases);
439                         }
440                     }
441                 } 
442                 else {
443                     float fixedHeight = cell.getFixedHeight();
444                     float rightLimit = cell.getRight() + xPos
445                             - cell.getEffectivePaddingRight();
446                     float leftLimit = cell.getLeft() + xPos
447                             + cell.getEffectivePaddingLeft();
448                     if (cell.isNoWrap()) {
449                         switch (cell.getHorizontalAlignment()) {
450                             case Element.ALIGN_CENTER:
451                                 rightLimit += 10000;
452                                 leftLimit -= 10000;
453                                 break;
454                             case Element.ALIGN_RIGHT:
455                                 if (cell.getRotation() == 180) {
456                                     rightLimit += RIGHT_LIMIT;
457                                 }
458                                 else {
459                                     leftLimit -= RIGHT_LIMIT;
460                                 }
461                                 break;
462                             default:
463                                 if (cell.getRotation() == 180) {
464                                     leftLimit -= RIGHT_LIMIT;
465                                 }
466                                 else {
467                                     rightLimit += RIGHT_LIMIT;
468                                 }
469                                 break;
470                         }
471                     }
472                     ColumnText ct = ColumnText.duplicate(cell.getColumn());
473                     ct.setCanvases(canvases);
474                     float bry = tly
475                             - (currentMaxHeight
476                             - cell.getEffectivePaddingTop() - cell.getEffectivePaddingBottom());
477                     if (fixedHeight > 0) {
478                         if (cell.getHeight() > currentMaxHeight) {
479                             tly = cell.getTop() + yPos - cell.getEffectivePaddingTop();
480                             bry = cell.getTop() + yPos - currentMaxHeight + cell.getEffectivePaddingBottom();
481                         }
482                     }
483                     if ((tly > bry || ct.zeroHeightElement()) && leftLimit < rightLimit) {
484                         ct.setSimpleColumn(leftLimit, bry - 0.001f,    rightLimit, tly);
485                         if (cell.getRotation() == 180) {
486                             float shx = leftLimit + rightLimit;
487                             float shy = yPos + yPos - currentMaxHeight + cell.getEffectivePaddingBottom() - cell.getEffectivePaddingTop();
488                             saveAndRotateCanvases(canvases, -1,0,0,-1,shx,shy);
489                         }
490                         try {
491                             ct.go();
492                         } catch (DocumentException e) {
493                             throw new ExceptionConverter(e);
494                         } finally {
495                             if (cell.getRotation() == 180) {
496                                 restoreCanvases(canvases);
497                             }
498                         }
499                     }
500                 }
501             }
502             PdfPCellEvent evt = cell.getCellEvent();
503             if (evt != null) {
504                 Rectangle rect = new Rectangle(cell.getLeft() + xPos, cell.getTop()
505                         + yPos - currentMaxHeight, cell.getRight() + xPos, cell.getTop()
506                         + yPos);
507                 evt.cellLayout(cell, rect, canvases);
508             }
509         }
510     }
511     
512     /**
513      * Checks if the dimensions of the columns were calculated.
514      * 
515      * @return true if the dimensions of the columns were calculated
516      */

517     public boolean isCalculated() {
518         return calculated;
519     }
520
521     /**
522      * Gets the maximum height of the row (i.e. of the 'highest' cell).
523      * 
524      * @return the maximum height of the row
525      */

526     public float getMaxHeights() {
527         if (calculated)
528             return maxHeight;
529         return calculateHeights();
530     }
531
532     /**
533      * Changes the maximum height of the row (to make it higher).
534      * (added by Jin-Hsia Yang)
535      * 
536      * @param maxHeight the new maximum height
537      */

538     public void setMaxHeights(float maxHeight) {
539         this.maxHeight = maxHeight;
540     }
541
542     //end add
543
544     float[] getEventWidth(float xPos) {
545         int n = 0;
546         for (int k = 0; k < cells.length; ++k) {
547             if (cells[k] != null)
548                 ++n;
549         }
550         float width[] = new float[n + 1];
551         n = 0;
552         width[n++] = xPos;
553         for (int k = 0; k < cells.length; ++k) {
554             if (cells[k] != null) {
555                 width[n] = width[n - 1] + cells[k].getWidth();
556                 ++n;
557             }
558         }
559         return width;
560     }
561
562     /**
563      * Splits a row to newHeight.
564      * The returned row is the remainder. It will return null if the newHeight
565      * was so small that only an empty row would result.
566      * 
567      * @param new_height    the new height
568      * @return the remainder row or null if the newHeight was so small that only
569      * an empty row would result
570      */

571     public PdfPRow splitRow(PdfPTable table, int rowIndex, float new_height) {
572         PdfPCell newCells[] = new PdfPCell[cells.length];
573         float fixHs[] = new float[cells.length];
574         float minHs[] = new float[cells.length];
575         boolean allEmpty = true;
576         for (int k = 0; k < cells.length; ++k) {
577             float newHeight = new_height;
578             PdfPCell cell = cells[k];
579             if (cell == null) {
580                 int index = rowIndex;
581                 if (table.rowSpanAbove(index, k)) {
582                     newHeight += table.getRowHeight(index);
583                     while (table.rowSpanAbove(--index, k)) {
584                         newHeight += table.getRowHeight(index);
585                     }
586                     PdfPRow row = table.getRow(index);
587                     if (row != null && row.getCells()[k] != null) {
588                         newCells[k] = new PdfPCell(row.getCells()[k]);
589                         newCells[k].consumeHeight(newHeight);
590                         newCells[k].setRowspan(row.getCells()[k].getRowspan() - rowIndex + index);
591                         allEmpty = false;
592                     }
593                 }
594                 continue;
595             }
596             fixHs[k] = cell.getFixedHeight();
597             minHs[k] = cell.getMinimumHeight();
598             Image img = cell.getImage();
599             PdfPCell newCell = new PdfPCell(cell);
600             if (img != null) {
601                 if (newHeight > cell.getEffectivePaddingBottom() + cell.getEffectivePaddingTop() + 2) {
602                     newCell.setPhrase(null);
603                     allEmpty = false;
604                 }
605             }
606             else {
607                 float y;
608                 ColumnText ct = ColumnText.duplicate(cell.getColumn());
609                 float left = cell.getLeft() + cell.getEffectivePaddingLeft();
610                 float bottom = cell.getTop() + cell.getEffectivePaddingBottom() - newHeight;
611                 float right = cell.getRight() - cell.getEffectivePaddingRight();
612                 float top = cell.getTop() - cell.getEffectivePaddingTop();
613                 switch (cell.getRotation()) {
614                     case 90:
615                     case 270:
616                         y = setColumn(ct, bottom, left, top, right);
617                         break;
618                     default:
619                         y = setColumn(ct, left, bottom, cell.isNoWrap() ? RIGHT_LIMIT : right, top);
620                         break;
621                 }
622                 int status;
623                 try {
624                     status = ct.go(true);
625                 }
626                 catch (DocumentException e) {
627                     throw new ExceptionConverter(e);
628                 }
629                 boolean thisEmpty = (ct.getYLine() == y);
630                 if (thisEmpty) {
631                     newCell.setColumn(ColumnText.duplicate(cell.getColumn()));
632                     ct.setFilledWidth(0);
633                 }
634                 else if ((status & ColumnText.NO_MORE_TEXT) == 0) {
635                     newCell.setColumn(ct);
636                     ct.setFilledWidth(0);
637                 }
638                 else
639                     newCell.setPhrase(null);
640                 allEmpty = (allEmpty && thisEmpty);
641             }
642             newCells[k] = newCell;
643             cell.setFixedHeight(newHeight);
644         }
645         if (allEmpty) {
646             for (int k = 0; k < cells.length; ++k) {
647                 PdfPCell cell = cells[k];
648                 if (cell == null)
649                     continue;
650                 if (fixHs[k] > 0)
651                     cell.setFixedHeight(fixHs[k]);
652                 else
653                     cell.setMinimumHeight(minHs[k]);
654             }
655             return null;
656         }
657         calculateHeights();
658         PdfPRow split = new PdfPRow(newCells);
659         split.widths = (float[]) widths.clone();
660         split.calculateHeights();
661         return split;
662     }
663     
664     /**
665      * Returns the array of cells in the row.
666      * Please be extremely careful with this method.
667      * Use the cells as read only objects.
668      * 
669      * @return    an array of cells
670      * @since    2.1.1
671      */

672     public PdfPCell[] getCells() {
673         return cells;
674     }
675 }
676