6058 |
17 Nov 20 |
nicklas |
1 |
package net.sf.basedb.reggie.pdf; |
6058 |
17 Nov 20 |
nicklas |
2 |
|
6058 |
17 Nov 20 |
nicklas |
3 |
import java.io.ByteArrayOutputStream; |
6058 |
17 Nov 20 |
nicklas |
4 |
import java.io.FileInputStream; |
6058 |
17 Nov 20 |
nicklas |
5 |
import java.io.IOException; |
6058 |
17 Nov 20 |
nicklas |
6 |
import java.io.InputStream; |
6058 |
17 Nov 20 |
nicklas |
7 |
import java.io.OutputStream; |
6092 |
14 Dec 20 |
nicklas |
8 |
import java.util.ArrayList; |
6449 |
20 Oct 21 |
nicklas |
9 |
import java.util.Date; |
6081 |
25 Nov 20 |
nicklas |
10 |
import java.util.HashMap; |
6092 |
14 Dec 20 |
nicklas |
11 |
import java.util.List; |
6081 |
25 Nov 20 |
nicklas |
12 |
import java.util.Map; |
6058 |
17 Nov 20 |
nicklas |
13 |
|
6061 |
18 Nov 20 |
nicklas |
14 |
import com.itextpdf.barcodes.Barcode128; |
6073 |
20 Nov 20 |
nicklas |
15 |
import com.itextpdf.io.font.FontNames; |
6058 |
17 Nov 20 |
nicklas |
16 |
import com.itextpdf.io.font.PdfEncodings; |
6058 |
17 Nov 20 |
nicklas |
17 |
import com.itextpdf.io.image.ImageData; |
6058 |
17 Nov 20 |
nicklas |
18 |
import com.itextpdf.io.image.ImageDataFactory; |
6060 |
17 Nov 20 |
nicklas |
19 |
import com.itextpdf.io.image.ImageType; |
6060 |
17 Nov 20 |
nicklas |
20 |
import com.itextpdf.io.image.ImageTypeDetector; |
6100 |
12 Jan 21 |
nicklas |
21 |
import com.itextpdf.io.source.PdfTokenizer; |
6100 |
12 Jan 21 |
nicklas |
22 |
import com.itextpdf.io.source.RandomAccessFileOrArray; |
6100 |
12 Jan 21 |
nicklas |
23 |
import com.itextpdf.io.source.RandomAccessSourceFactory; |
6058 |
17 Nov 20 |
nicklas |
24 |
import com.itextpdf.kernel.colors.Color; |
6058 |
17 Nov 20 |
nicklas |
25 |
import com.itextpdf.kernel.colors.ColorConstants; |
6058 |
17 Nov 20 |
nicklas |
26 |
import com.itextpdf.kernel.font.PdfFont; |
6058 |
17 Nov 20 |
nicklas |
27 |
import com.itextpdf.kernel.font.PdfFontFactory; |
6061 |
18 Nov 20 |
nicklas |
28 |
import com.itextpdf.kernel.geom.AffineTransform; |
6058 |
17 Nov 20 |
nicklas |
29 |
import com.itextpdf.kernel.geom.PageSize; |
6070 |
20 Nov 20 |
nicklas |
30 |
import com.itextpdf.kernel.geom.Point; |
6058 |
17 Nov 20 |
nicklas |
31 |
import com.itextpdf.kernel.geom.Rectangle; |
6058 |
17 Nov 20 |
nicklas |
32 |
import com.itextpdf.kernel.pdf.EncryptionConstants; |
6449 |
20 Oct 21 |
nicklas |
33 |
import com.itextpdf.kernel.pdf.PdfDate; |
6100 |
12 Jan 21 |
nicklas |
34 |
import com.itextpdf.kernel.pdf.PdfDictionary; |
6058 |
17 Nov 20 |
nicklas |
35 |
import com.itextpdf.kernel.pdf.PdfDocument; |
6058 |
17 Nov 20 |
nicklas |
36 |
import com.itextpdf.kernel.pdf.PdfDocumentInfo; |
6100 |
12 Jan 21 |
nicklas |
37 |
import com.itextpdf.kernel.pdf.PdfLiteral; |
6100 |
12 Jan 21 |
nicklas |
38 |
import com.itextpdf.kernel.pdf.PdfName; |
6100 |
12 Jan 21 |
nicklas |
39 |
import com.itextpdf.kernel.pdf.PdfObject; |
6058 |
17 Nov 20 |
nicklas |
40 |
import com.itextpdf.kernel.pdf.PdfPage; |
6058 |
17 Nov 20 |
nicklas |
41 |
import com.itextpdf.kernel.pdf.PdfReader; |
6100 |
12 Jan 21 |
nicklas |
42 |
import com.itextpdf.kernel.pdf.PdfStream; |
6058 |
17 Nov 20 |
nicklas |
43 |
import com.itextpdf.kernel.pdf.PdfWriter; |
6058 |
17 Nov 20 |
nicklas |
44 |
import com.itextpdf.kernel.pdf.WriterProperties; |
6058 |
17 Nov 20 |
nicklas |
45 |
import com.itextpdf.kernel.pdf.canvas.PdfCanvas; |
6060 |
17 Nov 20 |
nicklas |
46 |
import com.itextpdf.kernel.pdf.canvas.wmf.WmfImageData; |
6060 |
17 Nov 20 |
nicklas |
47 |
import com.itextpdf.kernel.pdf.canvas.wmf.WmfImageHelper; |
6075 |
23 Nov 20 |
nicklas |
48 |
import com.itextpdf.kernel.pdf.extgstate.PdfExtGState; |
6058 |
17 Nov 20 |
nicklas |
49 |
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject; |
6092 |
14 Dec 20 |
nicklas |
50 |
import com.itextpdf.pdfcleanup.PdfCleanUpLocation; |
6092 |
14 Dec 20 |
nicklas |
51 |
import com.itextpdf.pdfcleanup.PdfCleanUpTool; |
6092 |
14 Dec 20 |
nicklas |
52 |
import com.itextpdf.pdfcleanup.autosweep.PdfAutoSweep; |
6092 |
14 Dec 20 |
nicklas |
53 |
import com.itextpdf.pdfcleanup.autosweep.RegexBasedCleanupStrategy; |
6064 |
18 Nov 20 |
nicklas |
54 |
import com.itextpdf.svg.converter.SvgConverter; |
6058 |
17 Nov 20 |
nicklas |
55 |
|
6058 |
17 Nov 20 |
nicklas |
56 |
import net.sf.basedb.core.Application; |
6058 |
17 Nov 20 |
nicklas |
57 |
import net.sf.basedb.reggie.Reggie; |
6058 |
17 Nov 20 |
nicklas |
58 |
import net.sf.basedb.util.FileUtil; |
6058 |
17 Nov 20 |
nicklas |
59 |
|
6058 |
17 Nov 20 |
nicklas |
60 |
/** |
6058 |
17 Nov 20 |
nicklas |
Utility class for working with PDF documents using iText PDF version 7. |
6058 |
17 Nov 20 |
nicklas |
We always work with A4 documents (unless noted). |
6058 |
17 Nov 20 |
nicklas |
63 |
|
6058 |
17 Nov 20 |
nicklas |
@author nicklas |
6058 |
17 Nov 20 |
nicklas |
@since 4.28 |
6058 |
17 Nov 20 |
nicklas |
66 |
*/ |
6058 |
17 Nov 20 |
nicklas |
67 |
public class PdfUtil7 |
6058 |
17 Nov 20 |
nicklas |
68 |
{ |
6058 |
17 Nov 20 |
nicklas |
69 |
|
6062 |
18 Nov 20 |
nicklas |
70 |
/** |
6062 |
18 Nov 20 |
nicklas |
DPI value used in coordinate system for PDF. |
6062 |
18 Nov 20 |
nicklas |
72 |
*/ |
6062 |
18 Nov 20 |
nicklas |
73 |
public static final float DPI = 72; |
6062 |
18 Nov 20 |
nicklas |
74 |
|
6062 |
18 Nov 20 |
nicklas |
75 |
/** |
6062 |
18 Nov 20 |
nicklas |
Full with in points for an A4 PDF document (595). |
6062 |
18 Nov 20 |
nicklas |
77 |
*/ |
6062 |
18 Nov 20 |
nicklas |
78 |
public static final float FULL_WIDTH = PageSize.A4.getWidth(); |
6062 |
18 Nov 20 |
nicklas |
79 |
|
6062 |
18 Nov 20 |
nicklas |
80 |
/** |
6062 |
18 Nov 20 |
nicklas |
Full height in points for an A4 PDF document (842). |
6062 |
18 Nov 20 |
nicklas |
82 |
*/ |
6062 |
18 Nov 20 |
nicklas |
83 |
public static final float FULL_HEIGHT = PageSize.A4.getHeight(); |
6062 |
18 Nov 20 |
nicklas |
84 |
|
6062 |
18 Nov 20 |
nicklas |
85 |
|
6060 |
17 Nov 20 |
nicklas |
86 |
private String title; |
6060 |
17 Nov 20 |
nicklas |
87 |
private String creator; |
6058 |
17 Nov 20 |
nicklas |
88 |
|
6058 |
17 Nov 20 |
nicklas |
89 |
private PdfWriter writer; |
6058 |
17 Nov 20 |
nicklas |
90 |
private PdfDocument pdf; |
6058 |
17 Nov 20 |
nicklas |
91 |
private PdfPage page; |
6058 |
17 Nov 20 |
nicklas |
92 |
private PdfCanvas canvas; |
6060 |
17 Nov 20 |
nicklas |
93 |
|
6060 |
17 Nov 20 |
nicklas |
94 |
private PdfReader reader; |
6058 |
17 Nov 20 |
nicklas |
95 |
|
6058 |
17 Nov 20 |
nicklas |
96 |
private PdfFont defaultFont; |
6058 |
17 Nov 20 |
nicklas |
97 |
private PdfFont boldFont; |
6073 |
20 Nov 20 |
nicklas |
98 |
private PdfFont italicFont; |
6073 |
20 Nov 20 |
nicklas |
99 |
private PdfFont boldItalicFont; |
6073 |
20 Nov 20 |
nicklas |
100 |
|
6058 |
17 Nov 20 |
nicklas |
101 |
private Color currentColor = ColorConstants.BLACK; |
6058 |
17 Nov 20 |
nicklas |
102 |
private Color clearColor = ColorConstants.WHITE; |
6075 |
23 Nov 20 |
nicklas |
103 |
private float clearOpacity = Float.NaN; |
6092 |
14 Dec 20 |
nicklas |
104 |
private Color redactColor = ColorConstants.WHITE; |
6100 |
12 Jan 21 |
nicklas |
105 |
private float redactOpacity = Float.NaN; |
6058 |
17 Nov 20 |
nicklas |
106 |
|
6092 |
14 Dec 20 |
nicklas |
107 |
private Map<String, PdfDocument> docCache = new HashMap<>(); |
6092 |
14 Dec 20 |
nicklas |
108 |
private List<PdfCleanUpLocation> redactLocations = new ArrayList<>(); |
6081 |
25 Nov 20 |
nicklas |
109 |
|
6058 |
17 Nov 20 |
nicklas |
110 |
/** |
6058 |
17 Nov 20 |
nicklas |
Create a new PDF document. |
6058 |
17 Nov 20 |
nicklas |
112 |
*/ |
6058 |
17 Nov 20 |
nicklas |
113 |
public PdfUtil7(String title, String creator) |
6058 |
17 Nov 20 |
nicklas |
114 |
{ |
6058 |
17 Nov 20 |
nicklas |
115 |
this.title = title; |
6058 |
17 Nov 20 |
nicklas |
116 |
this.creator = creator; |
6058 |
17 Nov 20 |
nicklas |
117 |
this.defaultFont = getDefaultFont(); |
6058 |
17 Nov 20 |
nicklas |
118 |
} |
6058 |
17 Nov 20 |
nicklas |
119 |
|
6058 |
17 Nov 20 |
nicklas |
120 |
/** |
6060 |
17 Nov 20 |
nicklas |
Create a new PDF document that is a copy |
6060 |
17 Nov 20 |
nicklas |
of an existing PDF file. |
6060 |
17 Nov 20 |
nicklas |
123 |
*/ |
6060 |
17 Nov 20 |
nicklas |
124 |
public PdfUtil7(InputStream existing) |
6060 |
17 Nov 20 |
nicklas |
125 |
throws IOException |
6060 |
17 Nov 20 |
nicklas |
126 |
{ |
6060 |
17 Nov 20 |
nicklas |
127 |
this.reader = new PdfReader(existing); |
6060 |
17 Nov 20 |
nicklas |
128 |
this.defaultFont = getDefaultFont(); |
6060 |
17 Nov 20 |
nicklas |
129 |
} |
6060 |
17 Nov 20 |
nicklas |
130 |
|
6060 |
17 Nov 20 |
nicklas |
131 |
/** |
6058 |
17 Nov 20 |
nicklas |
Open the PDF document so that content can be added to it. |
6058 |
17 Nov 20 |
nicklas |
@param out The outputstream to write the PDF document to |
6058 |
17 Nov 20 |
nicklas |
134 |
*/ |
6058 |
17 Nov 20 |
nicklas |
135 |
public void open(OutputStream out) |
6058 |
17 Nov 20 |
nicklas |
136 |
{ |
6058 |
17 Nov 20 |
nicklas |
137 |
open(out, null); |
6058 |
17 Nov 20 |
nicklas |
138 |
} |
6058 |
17 Nov 20 |
nicklas |
139 |
|
6058 |
17 Nov 20 |
nicklas |
140 |
|
6058 |
17 Nov 20 |
nicklas |
141 |
/** |
6058 |
17 Nov 20 |
nicklas |
Open the PDF document so that content can be added to it. If a password |
6058 |
17 Nov 20 |
nicklas |
is specified the PDF is encrypted using AES-256 encryption. The password |
6058 |
17 Nov 20 |
nicklas |
is the owner password. A random string is generated for the user password. |
6058 |
17 Nov 20 |
nicklas |
145 |
|
6058 |
17 Nov 20 |
nicklas |
@param out The outputstream to write the PDF document to |
6058 |
17 Nov 20 |
nicklas |
@param password If not null, the pdf if password protected and encrypted |
6058 |
17 Nov 20 |
nicklas |
with AES 256. |
6058 |
17 Nov 20 |
nicklas |
149 |
*/ |
6058 |
17 Nov 20 |
nicklas |
150 |
public void open(OutputStream out, String password) |
6058 |
17 Nov 20 |
nicklas |
151 |
{ |
6058 |
17 Nov 20 |
nicklas |
152 |
WriterProperties wp = new WriterProperties(); |
6058 |
17 Nov 20 |
nicklas |
153 |
if (password != null) |
6058 |
17 Nov 20 |
nicklas |
154 |
{ |
6058 |
17 Nov 20 |
nicklas |
155 |
wp.setStandardEncryption(Application.generateRandomId(16).getBytes(), password.getBytes(), 0, EncryptionConstants.ENCRYPTION_AES_256); |
6058 |
17 Nov 20 |
nicklas |
156 |
} |
6060 |
17 Nov 20 |
nicklas |
157 |
writer = new PdfWriter(out, wp); |
6060 |
17 Nov 20 |
nicklas |
158 |
if (reader != null) |
6060 |
17 Nov 20 |
nicklas |
159 |
{ |
6060 |
17 Nov 20 |
nicklas |
160 |
pdf = new PdfDocument(reader, writer); |
6060 |
17 Nov 20 |
nicklas |
161 |
page = pdf.getFirstPage(); |
6060 |
17 Nov 20 |
nicklas |
162 |
canvas = new PdfCanvas(page); |
6060 |
17 Nov 20 |
nicklas |
163 |
} |
6060 |
17 Nov 20 |
nicklas |
164 |
else |
6060 |
17 Nov 20 |
nicklas |
165 |
{ |
6060 |
17 Nov 20 |
nicklas |
166 |
pdf = new PdfDocument(writer); |
6060 |
17 Nov 20 |
nicklas |
167 |
PdfDocumentInfo info = pdf.getDocumentInfo(); |
6060 |
17 Nov 20 |
nicklas |
168 |
info.setCreator("Reggie " + Reggie.VERSION + (creator != null ? " ("+creator+")" : "")); |
6060 |
17 Nov 20 |
nicklas |
169 |
info.setTitle(title); |
6060 |
17 Nov 20 |
nicklas |
170 |
newPage(); |
6060 |
17 Nov 20 |
nicklas |
171 |
} |
6058 |
17 Nov 20 |
nicklas |
172 |
} |
6058 |
17 Nov 20 |
nicklas |
173 |
|
6058 |
17 Nov 20 |
nicklas |
174 |
/** |
6100 |
12 Jan 21 |
nicklas |
Try to remove contents falling with the specified redact regions. |
6100 |
12 Jan 21 |
nicklas |
This code will check and parse the "content streams" on each page |
6100 |
12 Jan 21 |
nicklas |
and XObject streams. The code is not intentended for generic use |
6100 |
12 Jan 21 |
nicklas |
since it assumes a simple structure of the PDF document when it |
6100 |
12 Jan 21 |
nicklas |
comes to nesting and matrix transformations (eg. none!). |
6100 |
12 Jan 21 |
nicklas |
180 |
|
6100 |
12 Jan 21 |
nicklas |
It seems to work well with the current SCAN-B report but may break |
6100 |
12 Jan 21 |
nicklas |
if we change anything in templates, iText libraries, etc. |
6100 |
12 Jan 21 |
nicklas |
183 |
|
6100 |
12 Jan 21 |
nicklas |
Regions that has been selected for redaction, can optionally be painted |
6100 |
12 Jan 21 |
nicklas |
with "redactColor". Use "false" or a transparant color to make sure that |
6100 |
12 Jan 21 |
nicklas |
nothing is left that was expected to be removed. As a last resort, paint |
6100 |
12 Jan 21 |
nicklas |
with solid white to hide contents but NOT if the data is sensitive. |
6100 |
12 Jan 21 |
nicklas |
@since 4.30 |
6100 |
12 Jan 21 |
nicklas |
189 |
*/ |
6100 |
12 Jan 21 |
nicklas |
190 |
private void simpleRedact(boolean paintRegions) |
6100 |
12 Jan 21 |
nicklas |
191 |
throws IOException |
6100 |
12 Jan 21 |
nicklas |
192 |
{ |
6100 |
12 Jan 21 |
nicklas |
193 |
|
6100 |
12 Jan 21 |
nicklas |
194 |
for (int pageNo = 1; pageNo <= pdf.getNumberOfPages(); pageNo++) |
6100 |
12 Jan 21 |
nicklas |
195 |
{ |
6100 |
12 Jan 21 |
nicklas |
// Get regions on this page |
6100 |
12 Jan 21 |
nicklas |
197 |
List<PdfCleanUpLocation> redactRegions = new ArrayList<>(); |
6100 |
12 Jan 21 |
nicklas |
198 |
for (PdfCleanUpLocation loc : redactLocations) |
6100 |
12 Jan 21 |
nicklas |
199 |
{ |
6100 |
12 Jan 21 |
nicklas |
200 |
if (loc.getPage() == pageNo) redactRegions.add(loc); |
6100 |
12 Jan 21 |
nicklas |
201 |
} |
6100 |
12 Jan 21 |
nicklas |
202 |
if (redactRegions.size() == 0) continue; |
6100 |
12 Jan 21 |
nicklas |
203 |
|
6100 |
12 Jan 21 |
nicklas |
204 |
PdfPage page = pdf.getPage(pageNo); |
6100 |
12 Jan 21 |
nicklas |
205 |
|
6100 |
12 Jan 21 |
nicklas |
// Check content streams |
6100 |
12 Jan 21 |
nicklas |
207 |
for (int streamNo = 0; streamNo < page.getContentStreamCount(); streamNo++) |
6100 |
12 Jan 21 |
nicklas |
208 |
{ |
6100 |
12 Jan 21 |
nicklas |
//System.out.println("Page: " + pageNo + "; stream: " + streamNo); |
6100 |
12 Jan 21 |
nicklas |
210 |
cleanStream(page.getContentStream(streamNo), redactRegions); |
6100 |
12 Jan 21 |
nicklas |
211 |
} |
6100 |
12 Jan 21 |
nicklas |
212 |
|
6100 |
12 Jan 21 |
nicklas |
// Check xobject streams |
6100 |
12 Jan 21 |
nicklas |
// I hope it is safe to do this... since we have less control over them... |
6100 |
12 Jan 21 |
nicklas |
215 |
PdfDictionary resources = page.getPdfObject().getAsDictionary(PdfName.Resources); |
6100 |
12 Jan 21 |
nicklas |
216 |
if (resources != null) |
6100 |
12 Jan 21 |
nicklas |
217 |
{ |
6100 |
12 Jan 21 |
nicklas |
218 |
PdfDictionary xobjects = resources.getAsDictionary(PdfName.XObject); |
6100 |
12 Jan 21 |
nicklas |
219 |
if (xobjects != null) |
6100 |
12 Jan 21 |
nicklas |
220 |
{ |
6100 |
12 Jan 21 |
nicklas |
221 |
for (Map.Entry<PdfName, PdfObject> entry : xobjects.entrySet()) |
6100 |
12 Jan 21 |
nicklas |
222 |
{ |
6100 |
12 Jan 21 |
nicklas |
223 |
PdfObject obj = entry.getValue(); |
6100 |
12 Jan 21 |
nicklas |
224 |
if (!obj.isStream()) continue; |
6100 |
12 Jan 21 |
nicklas |
225 |
|
6100 |
12 Jan 21 |
nicklas |
//System.out.println("Page: " + pageNo + "; stream: " + entry.getKey()); |
6100 |
12 Jan 21 |
nicklas |
227 |
cleanStream((PdfStream)obj, redactRegions); |
6100 |
12 Jan 21 |
nicklas |
228 |
} |
6100 |
12 Jan 21 |
nicklas |
229 |
} |
6100 |
12 Jan 21 |
nicklas |
230 |
} |
6100 |
12 Jan 21 |
nicklas |
231 |
|
6100 |
12 Jan 21 |
nicklas |
// Paint regions |
6100 |
12 Jan 21 |
nicklas |
233 |
if (paintRegions) |
6100 |
12 Jan 21 |
nicklas |
234 |
{ |
6100 |
12 Jan 21 |
nicklas |
235 |
PdfCanvas canvas = new PdfCanvas(page); |
6100 |
12 Jan 21 |
nicklas |
236 |
canvas.saveState(); |
6100 |
12 Jan 21 |
nicklas |
237 |
if (!Float.isNaN(redactOpacity)) |
6100 |
12 Jan 21 |
nicklas |
238 |
{ |
6100 |
12 Jan 21 |
nicklas |
239 |
canvas.setExtGState(new PdfExtGState().setFillOpacity(redactOpacity)); |
6100 |
12 Jan 21 |
nicklas |
240 |
} |
6100 |
12 Jan 21 |
nicklas |
241 |
for (PdfCleanUpLocation loc : redactRegions) |
6100 |
12 Jan 21 |
nicklas |
242 |
{ |
6100 |
12 Jan 21 |
nicklas |
243 |
canvas.setFillColor(loc.getCleanUpColor()); |
6100 |
12 Jan 21 |
nicklas |
244 |
canvas.rectangle(loc.getRegion()); |
6100 |
12 Jan 21 |
nicklas |
245 |
canvas.fill(); |
6100 |
12 Jan 21 |
nicklas |
246 |
} |
6100 |
12 Jan 21 |
nicklas |
247 |
canvas.restoreState(); |
6100 |
12 Jan 21 |
nicklas |
248 |
} |
6100 |
12 Jan 21 |
nicklas |
249 |
} |
6100 |
12 Jan 21 |
nicklas |
250 |
} |
6100 |
12 Jan 21 |
nicklas |
251 |
|
6100 |
12 Jan 21 |
nicklas |
252 |
/** |
6100 |
12 Jan 21 |
nicklas |
A very simple cleaning procedure. We assume the PDF is structured with |
6100 |
12 Jan 21 |
nicklas |
a 'q' and 'Q' delimiting a single text or xobject. We check if we find a location |
6100 |
12 Jan 21 |
nicklas |
coordinate (either in 'Td' (for text) or 'cm' (for xobject) that falls withing one |
6100 |
12 Jan 21 |
nicklas |
of the redaction regions. If not, we copy the data between 'q' and 'Q' otherwise we |
6100 |
12 Jan 21 |
nicklas |
simply skip it. Note that this will not remove the referenced xobjects. |
6100 |
12 Jan 21 |
nicklas |
258 |
|
6100 |
12 Jan 21 |
nicklas |
NOTE! This is not a generic removal procedure. It has been tested to work with the PDF |
6100 |
12 Jan 21 |
nicklas |
generated by this class. Changes to the implementation or structure may cause this code |
6100 |
12 Jan 21 |
nicklas |
to stop working. |
6100 |
12 Jan 21 |
nicklas |
262 |
*/ |
6100 |
12 Jan 21 |
nicklas |
263 |
private void cleanStream(PdfStream stream, List<PdfCleanUpLocation> redactRegions) |
6100 |
12 Jan 21 |
nicklas |
264 |
throws IOException |
6100 |
12 Jan 21 |
nicklas |
265 |
{ |
6100 |
12 Jan 21 |
nicklas |
266 |
if (stream.getLength() <= 0) return; |
6100 |
12 Jan 21 |
nicklas |
267 |
|
6100 |
12 Jan 21 |
nicklas |
268 |
byte[] data = stream.getBytes(); |
6100 |
12 Jan 21 |
nicklas |
269 |
byte[] out = new byte[data.length]; |
6100 |
12 Jan 21 |
nicklas |
270 |
|
6100 |
12 Jan 21 |
nicklas |
271 |
PdfTokenizer tokenizer = new PdfTokenizer(new RandomAccessFileOrArray(new RandomAccessSourceFactory().createSource(data))); |
6100 |
12 Jan 21 |
nicklas |
272 |
|
6100 |
12 Jan 21 |
nicklas |
// Flags if we should copy and/or cut a range |
6100 |
12 Jan 21 |
nicklas |
274 |
boolean copy = true; |
6100 |
12 Jan 21 |
nicklas |
275 |
boolean cut = false; |
6100 |
12 Jan 21 |
nicklas |
// Current offsets in the in/out arrays |
6100 |
12 Jan 21 |
nicklas |
277 |
int offsetOut = 0; |
6100 |
12 Jan 21 |
nicklas |
278 |
int offsetData = 0; |
6100 |
12 Jan 21 |
nicklas |
// Operators the we need to use (eg. 'x' and 'y' coordinate of text or xobject) |
6100 |
12 Jan 21 |
nicklas |
280 |
String last1 = null; |
6100 |
12 Jan 21 |
nicklas |
281 |
String last2 = null; |
6100 |
12 Jan 21 |
nicklas |
282 |
|
6100 |
12 Jan 21 |
nicklas |
283 |
while (tokenizer.nextToken()) |
6100 |
12 Jan 21 |
nicklas |
284 |
{ |
6100 |
12 Jan 21 |
nicklas |
285 |
PdfLiteral literal = new PdfLiteral(tokenizer.getByteContent()); |
6100 |
12 Jan 21 |
nicklas |
286 |
String op = literal.toString(); |
6100 |
12 Jan 21 |
nicklas |
287 |
if ("q".equals(op)) |
6100 |
12 Jan 21 |
nicklas |
288 |
{ |
6100 |
12 Jan 21 |
nicklas |
// Stop copying if we see a 'q'. |
6100 |
12 Jan 21 |
nicklas |
290 |
copy = false; |
6100 |
12 Jan 21 |
nicklas |
291 |
} |
6100 |
12 Jan 21 |
nicklas |
292 |
else if ("Q".equals(op)) |
6100 |
12 Jan 21 |
nicklas |
293 |
{ |
6100 |
12 Jan 21 |
nicklas |
294 |
if (cut) |
6100 |
12 Jan 21 |
nicklas |
295 |
{ |
6100 |
12 Jan 21 |
nicklas |
// If cutting, we move the data offset to current location |
6100 |
12 Jan 21 |
nicklas |
// which means the copy will see 0 bytes |
6100 |
12 Jan 21 |
nicklas |
298 |
offsetData = (int)tokenizer.getPosition(); |
6100 |
12 Jan 21 |
nicklas |
299 |
cut = false; |
6100 |
12 Jan 21 |
nicklas |
300 |
} |
6100 |
12 Jan 21 |
nicklas |
301 |
copy = true; |
6100 |
12 Jan 21 |
nicklas |
302 |
} |
6100 |
12 Jan 21 |
nicklas |
303 |
else if ("Td".equals(op) || "cm".equals(op)) |
6100 |
12 Jan 21 |
nicklas |
304 |
{ |
6100 |
12 Jan 21 |
nicklas |
// We get 'x y Td' or 'a b c d x y cm' |
6100 |
12 Jan 21 |
nicklas |
// In either case, the x and y coordinates are the previous two tokens |
6100 |
12 Jan 21 |
nicklas |
307 |
float x = Float.parseFloat(last2); |
6100 |
12 Jan 21 |
nicklas |
308 |
float y = Float.parseFloat(last1); |
6100 |
12 Jan 21 |
nicklas |
309 |
Rectangle r = new Rectangle(x, y, 0, 0); |
6100 |
12 Jan 21 |
nicklas |
// System.out.println("Checking: " + op + ": " + x +"," + y); |
6100 |
12 Jan 21 |
nicklas |
311 |
for (PdfCleanUpLocation loc : redactRegions) |
6100 |
12 Jan 21 |
nicklas |
312 |
{ |
6100 |
12 Jan 21 |
nicklas |
313 |
if (loc.getRegion().overlaps(r, -0.2f)) |
6100 |
12 Jan 21 |
nicklas |
314 |
{ |
6100 |
12 Jan 21 |
nicklas |
// System.out.println("Cutting: " + op + ": " + x +"," + y); |
6100 |
12 Jan 21 |
nicklas |
316 |
cut = true; |
6100 |
12 Jan 21 |
nicklas |
317 |
break; |
6100 |
12 Jan 21 |
nicklas |
318 |
} |
6100 |
12 Jan 21 |
nicklas |
319 |
} |
6100 |
12 Jan 21 |
nicklas |
320 |
} |
6100 |
12 Jan 21 |
nicklas |
// Keep track of the last two operators since they are the 'x' and 'y' coordinates when we see 'Td' or 'cm' |
6100 |
12 Jan 21 |
nicklas |
322 |
last2 = last1; |
6100 |
12 Jan 21 |
nicklas |
323 |
last1 = op; |
6100 |
12 Jan 21 |
nicklas |
324 |
|
6100 |
12 Jan 21 |
nicklas |
325 |
if (copy) |
6100 |
12 Jan 21 |
nicklas |
326 |
{ |
6100 |
12 Jan 21 |
nicklas |
// Copy from data[] from stored offset to current location |
6100 |
12 Jan 21 |
nicklas |
328 |
int length = (int)tokenizer.getPosition() - offsetData; |
6100 |
12 Jan 21 |
nicklas |
// System.out.println("copy:" + op + "; " + offsetData + "->"+offsetOut + ": " + length); |
6100 |
12 Jan 21 |
nicklas |
330 |
if (length > 0) |
6100 |
12 Jan 21 |
nicklas |
331 |
{ |
6100 |
12 Jan 21 |
nicklas |
332 |
System.arraycopy(data, offsetData, out, offsetOut, length); |
6100 |
12 Jan 21 |
nicklas |
333 |
offsetData += length; |
6100 |
12 Jan 21 |
nicklas |
334 |
offsetOut += length; |
6100 |
12 Jan 21 |
nicklas |
335 |
} |
6100 |
12 Jan 21 |
nicklas |
336 |
} |
6100 |
12 Jan 21 |
nicklas |
337 |
} |
6100 |
12 Jan 21 |
nicklas |
338 |
|
6100 |
12 Jan 21 |
nicklas |
// Repace the content with the redacted data |
6100 |
12 Jan 21 |
nicklas |
340 |
if (offsetOut < offsetData) |
6100 |
12 Jan 21 |
nicklas |
341 |
{ |
6100 |
12 Jan 21 |
nicklas |
342 |
byte[] tmp = new byte[offsetOut]; |
6100 |
12 Jan 21 |
nicklas |
343 |
System.arraycopy(out, 0, tmp, 0, offsetOut); |
6100 |
12 Jan 21 |
nicklas |
344 |
stream.setData(tmp); |
6100 |
12 Jan 21 |
nicklas |
345 |
} |
6100 |
12 Jan 21 |
nicklas |
346 |
} |
6100 |
12 Jan 21 |
nicklas |
347 |
|
6100 |
12 Jan 21 |
nicklas |
348 |
/** |
6092 |
14 Dec 20 |
nicklas |
Set the title of the PDF documen. |
6092 |
14 Dec 20 |
nicklas |
@since 4.29 |
6092 |
14 Dec 20 |
nicklas |
351 |
*/ |
6100 |
12 Jan 21 |
nicklas |
352 |
public void setTitle(String title) |
6092 |
14 Dec 20 |
nicklas |
353 |
{ |
6092 |
14 Dec 20 |
nicklas |
354 |
pdf.getDocumentInfo().setTitle(title); |
6092 |
14 Dec 20 |
nicklas |
355 |
} |
6092 |
14 Dec 20 |
nicklas |
356 |
|
6449 |
20 Oct 21 |
nicklas |
357 |
public Date getCreationDate() |
6449 |
20 Oct 21 |
nicklas |
358 |
{ |
6449 |
20 Oct 21 |
nicklas |
359 |
String d = pdf.getDocumentInfo().getMoreInfo(PdfName.CreationDate.getValue()); |
6449 |
20 Oct 21 |
nicklas |
360 |
return PdfDate.decode(d).getTime(); |
6449 |
20 Oct 21 |
nicklas |
361 |
} |
6449 |
20 Oct 21 |
nicklas |
362 |
|
6092 |
14 Dec 20 |
nicklas |
363 |
/** |
6058 |
17 Nov 20 |
nicklas |
Create a new page in the existing document. |
6058 |
17 Nov 20 |
nicklas |
365 |
*/ |
6058 |
17 Nov 20 |
nicklas |
366 |
public void newPage() |
6058 |
17 Nov 20 |
nicklas |
367 |
{ |
6058 |
17 Nov 20 |
nicklas |
368 |
if (canvas != null) canvas.release(); |
6058 |
17 Nov 20 |
nicklas |
369 |
page = pdf.addNewPage(PageSize.A4); |
6058 |
17 Nov 20 |
nicklas |
370 |
canvas = new PdfCanvas(page); |
6061 |
18 Nov 20 |
nicklas |
371 |
setColor(currentColor); |
6058 |
17 Nov 20 |
nicklas |
372 |
} |
6058 |
17 Nov 20 |
nicklas |
373 |
|
6092 |
14 Dec 20 |
nicklas |
374 |
/** |
6092 |
14 Dec 20 |
nicklas |
Change the current page to the give page. If the page |
6092 |
14 Dec 20 |
nicklas |
doesn't exists, the current page is kept as it is. |
6092 |
14 Dec 20 |
nicklas |
@return TRUE if the page was change, FALSE if not |
6092 |
14 Dec 20 |
nicklas |
@since 4.29 |
6092 |
14 Dec 20 |
nicklas |
379 |
*/ |
6092 |
14 Dec 20 |
nicklas |
380 |
public boolean setCurrentPage(int pageNo) |
6092 |
14 Dec 20 |
nicklas |
381 |
{ |
6092 |
14 Dec 20 |
nicklas |
382 |
PdfPage tmp = pdf.getPage(pageNo); |
6092 |
14 Dec 20 |
nicklas |
383 |
if (tmp != null) |
6092 |
14 Dec 20 |
nicklas |
384 |
{ |
6092 |
14 Dec 20 |
nicklas |
385 |
page = tmp; |
6092 |
14 Dec 20 |
nicklas |
386 |
if (canvas != null) canvas.release(); |
6092 |
14 Dec 20 |
nicklas |
387 |
canvas = new PdfCanvas(page); |
6092 |
14 Dec 20 |
nicklas |
388 |
setColor(currentColor); |
6092 |
14 Dec 20 |
nicklas |
389 |
} |
6092 |
14 Dec 20 |
nicklas |
390 |
return tmp != null; |
6092 |
14 Dec 20 |
nicklas |
391 |
} |
6092 |
14 Dec 20 |
nicklas |
392 |
|
6092 |
14 Dec 20 |
nicklas |
393 |
/** |
6092 |
14 Dec 20 |
nicklas |
Get the page number of the current page. |
6092 |
14 Dec 20 |
nicklas |
@since 4.29 |
6092 |
14 Dec 20 |
nicklas |
396 |
*/ |
6092 |
14 Dec 20 |
nicklas |
397 |
public int getCurrentPageNumber() |
6092 |
14 Dec 20 |
nicklas |
398 |
{ |
6092 |
14 Dec 20 |
nicklas |
399 |
return pdf.getPageNumber(page); |
6092 |
14 Dec 20 |
nicklas |
400 |
} |
6092 |
14 Dec 20 |
nicklas |
401 |
|
6092 |
14 Dec 20 |
nicklas |
402 |
/** |
6092 |
14 Dec 20 |
nicklas |
Get the number of pages in the current PDF document. |
6092 |
14 Dec 20 |
nicklas |
@since 4.29 |
6092 |
14 Dec 20 |
nicklas |
405 |
*/ |
6092 |
14 Dec 20 |
nicklas |
406 |
public int getNumPages() |
6092 |
14 Dec 20 |
nicklas |
407 |
{ |
6092 |
14 Dec 20 |
nicklas |
408 |
return pdf.getNumberOfPages(); |
6092 |
14 Dec 20 |
nicklas |
409 |
} |
6092 |
14 Dec 20 |
nicklas |
410 |
|
6061 |
18 Nov 20 |
nicklas |
411 |
public void removePage(int pageNo) |
6061 |
18 Nov 20 |
nicklas |
412 |
{ |
6061 |
18 Nov 20 |
nicklas |
413 |
pdf.removePage(pageNo); |
6061 |
18 Nov 20 |
nicklas |
414 |
} |
6061 |
18 Nov 20 |
nicklas |
415 |
|
6073 |
20 Nov 20 |
nicklas |
416 |
public PdfFont loadFont(String path) |
6073 |
20 Nov 20 |
nicklas |
417 |
{ |
6073 |
20 Nov 20 |
nicklas |
418 |
try |
6073 |
20 Nov 20 |
nicklas |
419 |
{ |
6073 |
20 Nov 20 |
nicklas |
420 |
return PdfFontFactory.createFont(path, PdfEncodings.IDENTITY_H, true); |
6073 |
20 Nov 20 |
nicklas |
421 |
} |
6073 |
20 Nov 20 |
nicklas |
422 |
catch (IOException ex) |
6073 |
20 Nov 20 |
nicklas |
423 |
{ |
6073 |
20 Nov 20 |
nicklas |
424 |
throw new RuntimeException(ex); |
6073 |
20 Nov 20 |
nicklas |
425 |
} |
6073 |
20 Nov 20 |
nicklas |
426 |
} |
6073 |
20 Nov 20 |
nicklas |
427 |
|
6058 |
17 Nov 20 |
nicklas |
428 |
/** |
6073 |
20 Nov 20 |
nicklas |
Get the default font for text elements. |
6058 |
17 Nov 20 |
nicklas |
430 |
*/ |
6058 |
17 Nov 20 |
nicklas |
431 |
public PdfFont getDefaultFont() |
6058 |
17 Nov 20 |
nicklas |
432 |
{ |
6073 |
20 Nov 20 |
nicklas |
433 |
if (defaultFont == null) |
6058 |
17 Nov 20 |
nicklas |
434 |
{ |
6073 |
20 Nov 20 |
nicklas |
435 |
defaultFont = loadFont("/META-INF/fonts/OpenSans-Regular.ttf"); |
6058 |
17 Nov 20 |
nicklas |
436 |
} |
6058 |
17 Nov 20 |
nicklas |
437 |
return defaultFont; |
6058 |
17 Nov 20 |
nicklas |
438 |
} |
6058 |
17 Nov 20 |
nicklas |
439 |
|
6058 |
17 Nov 20 |
nicklas |
440 |
/** |
6073 |
20 Nov 20 |
nicklas |
Get the default bold font for text elements. |
6058 |
17 Nov 20 |
nicklas |
442 |
*/ |
6058 |
17 Nov 20 |
nicklas |
443 |
public PdfFont getBoldFont() |
6058 |
17 Nov 20 |
nicklas |
444 |
{ |
6058 |
17 Nov 20 |
nicklas |
445 |
if (boldFont == null) |
6058 |
17 Nov 20 |
nicklas |
446 |
{ |
6073 |
20 Nov 20 |
nicklas |
447 |
boldFont = loadFont("/META-INF/fonts/OpenSans-Bold.ttf"); |
6058 |
17 Nov 20 |
nicklas |
448 |
} |
6058 |
17 Nov 20 |
nicklas |
449 |
return boldFont; |
6058 |
17 Nov 20 |
nicklas |
450 |
} |
6058 |
17 Nov 20 |
nicklas |
451 |
|
6058 |
17 Nov 20 |
nicklas |
452 |
/** |
6073 |
20 Nov 20 |
nicklas |
Get the default italic font for text elements. |
6073 |
20 Nov 20 |
nicklas |
454 |
*/ |
6073 |
20 Nov 20 |
nicklas |
455 |
public PdfFont getItalicFont() |
6073 |
20 Nov 20 |
nicklas |
456 |
{ |
6073 |
20 Nov 20 |
nicklas |
457 |
if (italicFont == null) |
6073 |
20 Nov 20 |
nicklas |
458 |
{ |
6073 |
20 Nov 20 |
nicklas |
459 |
italicFont = loadFont("/META-INF/fonts/OpenSans-Italic.ttf"); |
6073 |
20 Nov 20 |
nicklas |
460 |
} |
6073 |
20 Nov 20 |
nicklas |
461 |
return italicFont; |
6073 |
20 Nov 20 |
nicklas |
462 |
} |
6073 |
20 Nov 20 |
nicklas |
463 |
|
6073 |
20 Nov 20 |
nicklas |
464 |
/** |
6073 |
20 Nov 20 |
nicklas |
Get the default bold and italic font for text elements. |
6073 |
20 Nov 20 |
nicklas |
466 |
*/ |
6073 |
20 Nov 20 |
nicklas |
467 |
public PdfFont getBoldItalicFont() |
6073 |
20 Nov 20 |
nicklas |
468 |
{ |
6073 |
20 Nov 20 |
nicklas |
469 |
if (boldItalicFont == null) |
6073 |
20 Nov 20 |
nicklas |
470 |
{ |
6073 |
20 Nov 20 |
nicklas |
471 |
boldItalicFont = loadFont("/META-INF/fonts/OpenSans-BoldItalic.ttf"); |
6073 |
20 Nov 20 |
nicklas |
472 |
} |
6073 |
20 Nov 20 |
nicklas |
473 |
return boldItalicFont; |
6073 |
20 Nov 20 |
nicklas |
474 |
} |
6073 |
20 Nov 20 |
nicklas |
475 |
|
6073 |
20 Nov 20 |
nicklas |
476 |
|
6073 |
20 Nov 20 |
nicklas |
477 |
/** |
6058 |
17 Nov 20 |
nicklas |
Close the PDF document. |
6058 |
17 Nov 20 |
nicklas |
479 |
*/ |
6058 |
17 Nov 20 |
nicklas |
480 |
public void close() |
6058 |
17 Nov 20 |
nicklas |
481 |
{ |
6058 |
17 Nov 20 |
nicklas |
482 |
if (canvas != null) canvas.release(); |
6100 |
12 Jan 21 |
nicklas |
483 |
FileUtil.close(pdf); |
6100 |
12 Jan 21 |
nicklas |
484 |
FileUtil.close(writer); |
6100 |
12 Jan 21 |
nicklas |
485 |
for (PdfDocument doc : docCache.values()) |
6100 |
12 Jan 21 |
nicklas |
486 |
{ |
6100 |
12 Jan 21 |
nicklas |
487 |
FileUtil.close(doc); |
6100 |
12 Jan 21 |
nicklas |
488 |
} |
6100 |
12 Jan 21 |
nicklas |
489 |
} |
6100 |
12 Jan 21 |
nicklas |
490 |
|
6100 |
12 Jan 21 |
nicklas |
491 |
/** |
6100 |
12 Jan 21 |
nicklas |
Apply redactions and close the PDF document. |
6100 |
12 Jan 21 |
nicklas |
@param useSimpleRedact TRUE to use the simple redact, FALSE to use pdfSweep |
6100 |
12 Jan 21 |
nicklas |
@since 4.30 |
6100 |
12 Jan 21 |
nicklas |
495 |
*/ |
6100 |
12 Jan 21 |
nicklas |
496 |
public void redactAndClose(boolean useSimpleRedact, boolean paintRegions) |
6100 |
12 Jan 21 |
nicklas |
497 |
{ |
6100 |
12 Jan 21 |
nicklas |
498 |
if (canvas != null) |
6100 |
12 Jan 21 |
nicklas |
499 |
{ |
6100 |
12 Jan 21 |
nicklas |
500 |
canvas.release(); |
6100 |
12 Jan 21 |
nicklas |
501 |
canvas = null; |
6100 |
12 Jan 21 |
nicklas |
502 |
} |
6092 |
14 Dec 20 |
nicklas |
503 |
if (redactLocations.size() > 0) |
6092 |
14 Dec 20 |
nicklas |
504 |
{ |
6092 |
14 Dec 20 |
nicklas |
505 |
try |
6092 |
14 Dec 20 |
nicklas |
506 |
{ |
6100 |
12 Jan 21 |
nicklas |
507 |
if (useSimpleRedact) |
6100 |
12 Jan 21 |
nicklas |
508 |
{ |
6100 |
12 Jan 21 |
nicklas |
509 |
simpleRedact(paintRegions); |
6100 |
12 Jan 21 |
nicklas |
510 |
} |
6100 |
12 Jan 21 |
nicklas |
511 |
else |
6100 |
12 Jan 21 |
nicklas |
512 |
{ |
6100 |
12 Jan 21 |
nicklas |
513 |
new PdfCleanUpTool(pdf, redactLocations).cleanUp(); |
6100 |
12 Jan 21 |
nicklas |
514 |
} |
6092 |
14 Dec 20 |
nicklas |
515 |
} |
6092 |
14 Dec 20 |
nicklas |
516 |
catch (IOException ex) |
6092 |
14 Dec 20 |
nicklas |
517 |
{ |
6092 |
14 Dec 20 |
nicklas |
518 |
throw new RuntimeException(ex); |
6092 |
14 Dec 20 |
nicklas |
519 |
} |
6092 |
14 Dec 20 |
nicklas |
520 |
} |
6100 |
12 Jan 21 |
nicklas |
521 |
close(); |
6058 |
17 Nov 20 |
nicklas |
522 |
} |
6058 |
17 Nov 20 |
nicklas |
523 |
|
6100 |
12 Jan 21 |
nicklas |
524 |
|
6100 |
12 Jan 21 |
nicklas |
525 |
|
6058 |
17 Nov 20 |
nicklas |
526 |
/** |
6058 |
17 Nov 20 |
nicklas |
Set the color to use for text and other affected elements. |
6058 |
17 Nov 20 |
nicklas |
528 |
*/ |
6058 |
17 Nov 20 |
nicklas |
529 |
public void setColor(Color color) |
6058 |
17 Nov 20 |
nicklas |
530 |
{ |
6058 |
17 Nov 20 |
nicklas |
531 |
this.currentColor = color; |
6058 |
17 Nov 20 |
nicklas |
532 |
canvas.setFillColor(color); |
6058 |
17 Nov 20 |
nicklas |
533 |
canvas.setStrokeColor(color); |
6058 |
17 Nov 20 |
nicklas |
534 |
} |
6058 |
17 Nov 20 |
nicklas |
535 |
|
6058 |
17 Nov 20 |
nicklas |
536 |
/** |
6092 |
14 Dec 20 |
nicklas |
Set the color to use for marking redacted areas. The |
6092 |
14 Dec 20 |
nicklas |
default color is white. |
6100 |
12 Jan 21 |
nicklas |
@since 4.29, 4.30 |
6092 |
14 Dec 20 |
nicklas |
540 |
*/ |
6100 |
12 Jan 21 |
nicklas |
541 |
public void setRedactColor(Color color, float opacity) |
6092 |
14 Dec 20 |
nicklas |
542 |
{ |
6092 |
14 Dec 20 |
nicklas |
543 |
this.redactColor = color; |
6100 |
12 Jan 21 |
nicklas |
544 |
this.redactOpacity = opacity; |
6092 |
14 Dec 20 |
nicklas |
545 |
} |
6092 |
14 Dec 20 |
nicklas |
546 |
|
6092 |
14 Dec 20 |
nicklas |
547 |
/** |
6069 |
19 Nov 20 |
nicklas |
Add text to the document using default options. |
6069 |
19 Nov 20 |
nicklas |
549 |
|
6069 |
19 Nov 20 |
nicklas |
@param text The text to add |
6069 |
19 Nov 20 |
nicklas |
@param fontSize Font size to use |
6069 |
19 Nov 20 |
nicklas |
@param x X coordinate in points |
6069 |
19 Nov 20 |
nicklas |
@param y Y coordinate in points |
6092 |
14 Dec 20 |
nicklas |
@return The font-size used (can be changed due to {@link Options#maxTextWidth(float)} setting) |
6069 |
19 Nov 20 |
nicklas |
555 |
*/ |
6092 |
14 Dec 20 |
nicklas |
556 |
public float addText(String text, float fontSize, float x, float y) |
6069 |
19 Nov 20 |
nicklas |
557 |
{ |
6092 |
14 Dec 20 |
nicklas |
558 |
return addText(text, fontSize, x, y, Options.DEFAULT); |
6069 |
19 Nov 20 |
nicklas |
559 |
} |
6069 |
19 Nov 20 |
nicklas |
560 |
|
6069 |
19 Nov 20 |
nicklas |
561 |
/** |
6069 |
19 Nov 20 |
nicklas |
Add text to the document using other options. |
6069 |
19 Nov 20 |
nicklas |
563 |
|
6069 |
19 Nov 20 |
nicklas |
@param text The text to add |
6069 |
19 Nov 20 |
nicklas |
@param fontSize Font size to use |
6069 |
19 Nov 20 |
nicklas |
@param x X coordinate in points |
6069 |
19 Nov 20 |
nicklas |
@param y Y coordinate in points |
6069 |
19 Nov 20 |
nicklas |
@param option Options for the text |
6092 |
14 Dec 20 |
nicklas |
@return The font-size used (can be changed due to {@link Options#maxTextWidth(float)} setting) |
6069 |
19 Nov 20 |
nicklas |
570 |
*/ |
6092 |
14 Dec 20 |
nicklas |
571 |
public float addText(String text, float fontSize, float x, float y, Options options) |
6069 |
19 Nov 20 |
nicklas |
572 |
{ |
6092 |
14 Dec 20 |
nicklas |
573 |
if (text == null || text.equals("")) return fontSize; |
6069 |
19 Nov 20 |
nicklas |
574 |
if (options == null) options = Options.DEFAULT; |
6069 |
19 Nov 20 |
nicklas |
575 |
|
6073 |
20 Nov 20 |
nicklas |
576 |
PdfFont font = options.font; |
6073 |
20 Nov 20 |
nicklas |
577 |
if (font == null) |
6073 |
20 Nov 20 |
nicklas |
578 |
{ |
6073 |
20 Nov 20 |
nicklas |
579 |
if (options.italic) |
6073 |
20 Nov 20 |
nicklas |
580 |
{ |
6073 |
20 Nov 20 |
nicklas |
581 |
font = options.bold ? getBoldItalicFont() : getItalicFont(); |
6073 |
20 Nov 20 |
nicklas |
582 |
} |
6073 |
20 Nov 20 |
nicklas |
583 |
else |
6073 |
20 Nov 20 |
nicklas |
584 |
{ |
6073 |
20 Nov 20 |
nicklas |
585 |
font = options.bold ? getBoldFont() : getDefaultFont(); |
6073 |
20 Nov 20 |
nicklas |
586 |
} |
6073 |
20 Nov 20 |
nicklas |
587 |
} |
6070 |
20 Nov 20 |
nicklas |
588 |
|
6078 |
24 Nov 20 |
nicklas |
// Adjust fontSize and text if it is wider than maxTextWidth |
6078 |
24 Nov 20 |
nicklas |
590 |
if (!Float.isNaN(options.maxTextWidth)) |
6078 |
24 Nov 20 |
nicklas |
591 |
{ |
6078 |
24 Nov 20 |
nicklas |
592 |
float minFontSize = fontSize * options.minFontSizeFraction; |
6078 |
24 Nov 20 |
nicklas |
593 |
String suffix = null; |
6078 |
24 Nov 20 |
nicklas |
594 |
while (options.maxTextWidth < font.getWidth(text, fontSize)) |
6078 |
24 Nov 20 |
nicklas |
595 |
{ |
6078 |
24 Nov 20 |
nicklas |
596 |
if (fontSize > minFontSize) |
6078 |
24 Nov 20 |
nicklas |
597 |
{ |
6078 |
24 Nov 20 |
nicklas |
// Decrease font size as long as it is larger than the min font size |
6078 |
24 Nov 20 |
nicklas |
599 |
fontSize -= 0.5; |
6078 |
24 Nov 20 |
nicklas |
600 |
} |
6078 |
24 Nov 20 |
nicklas |
601 |
else |
6078 |
24 Nov 20 |
nicklas |
602 |
{ |
6078 |
24 Nov 20 |
nicklas |
// After that, we remove characters from the end of the text |
6078 |
24 Nov 20 |
nicklas |
604 |
text = text.substring(0, text.length()-1); |
6078 |
24 Nov 20 |
nicklas |
605 |
suffix = "…"; |
6078 |
24 Nov 20 |
nicklas |
606 |
} |
6078 |
24 Nov 20 |
nicklas |
607 |
} |
6078 |
24 Nov 20 |
nicklas |
608 |
if (suffix != null) text += suffix; |
6078 |
24 Nov 20 |
nicklas |
609 |
} |
6078 |
24 Nov 20 |
nicklas |
610 |
|
6070 |
20 Nov 20 |
nicklas |
611 |
AffineTransform t = null; |
6070 |
20 Nov 20 |
nicklas |
612 |
if (!Float.isNaN(options.rotation)) |
6070 |
20 Nov 20 |
nicklas |
613 |
{ |
6070 |
20 Nov 20 |
nicklas |
614 |
float textWidth = font.getWidth(text, fontSize); |
6070 |
20 Nov 20 |
nicklas |
615 |
t = AffineTransform.getTranslateInstance(x, y); |
6070 |
20 Nov 20 |
nicklas |
616 |
t.rotate(options.rotation * Math.PI / 180); |
6070 |
20 Nov 20 |
nicklas |
617 |
t.translate(options.align.adjustX(0, textWidth), options.align.adjustY(0, fontSize)); |
6070 |
20 Nov 20 |
nicklas |
618 |
} |
6070 |
20 Nov 20 |
nicklas |
619 |
else |
6070 |
20 Nov 20 |
nicklas |
620 |
{ |
6070 |
20 Nov 20 |
nicklas |
621 |
x = options.align.adjustX(x, font, text, fontSize); |
6070 |
20 Nov 20 |
nicklas |
622 |
y = options.align.adjustY(y, fontSize); |
6070 |
20 Nov 20 |
nicklas |
623 |
} |
6069 |
19 Nov 20 |
nicklas |
624 |
|
6069 |
19 Nov 20 |
nicklas |
625 |
canvas.saveState(); |
6069 |
19 Nov 20 |
nicklas |
626 |
if (options.color != null) canvas.setFillColor(options.color); |
6069 |
19 Nov 20 |
nicklas |
627 |
|
6069 |
19 Nov 20 |
nicklas |
628 |
if (options.underline) |
6069 |
19 Nov 20 |
nicklas |
629 |
{ |
6070 |
20 Nov 20 |
nicklas |
630 |
float yOffset = -0.5f*(1+fontSize/12f); |
6069 |
19 Nov 20 |
nicklas |
631 |
float width = font.getWidth(text, fontSize); |
6070 |
20 Nov 20 |
nicklas |
632 |
Point p1, p2; |
6070 |
20 Nov 20 |
nicklas |
633 |
if (t != null) |
6070 |
20 Nov 20 |
nicklas |
634 |
{ |
6070 |
20 Nov 20 |
nicklas |
635 |
p1 = t.transform(new Point(0, yOffset), null); |
6070 |
20 Nov 20 |
nicklas |
636 |
p2 = t.transform(new Point (width, yOffset), null); |
6070 |
20 Nov 20 |
nicklas |
637 |
} |
6070 |
20 Nov 20 |
nicklas |
638 |
else |
6070 |
20 Nov 20 |
nicklas |
639 |
{ |
6070 |
20 Nov 20 |
nicklas |
640 |
p1 = new Point(x, y+yOffset); |
6070 |
20 Nov 20 |
nicklas |
641 |
p2 = new Point(x+width, p1.y); |
6070 |
20 Nov 20 |
nicklas |
642 |
} |
6069 |
19 Nov 20 |
nicklas |
643 |
if (options.color != null) canvas.setStrokeColor(options.color); |
6069 |
19 Nov 20 |
nicklas |
644 |
canvas.setLineWidth(Math.max(0.2f, (options.bold ? 0.85f : 0.5f) * fontSize / 12)); |
6070 |
20 Nov 20 |
nicklas |
645 |
canvas.moveTo(p1.x, p1.y); |
6070 |
20 Nov 20 |
nicklas |
646 |
canvas.lineTo(p2.x, p2.y); |
6069 |
19 Nov 20 |
nicklas |
647 |
canvas.stroke(); |
6069 |
19 Nov 20 |
nicklas |
648 |
} |
6069 |
19 Nov 20 |
nicklas |
649 |
|
6069 |
19 Nov 20 |
nicklas |
650 |
canvas.beginText(); |
6069 |
19 Nov 20 |
nicklas |
651 |
canvas.setFontAndSize(font, fontSize); |
6070 |
20 Nov 20 |
nicklas |
652 |
if (t != null) |
6070 |
20 Nov 20 |
nicklas |
653 |
{ |
6070 |
20 Nov 20 |
nicklas |
654 |
canvas.setTextMatrix(t); |
6070 |
20 Nov 20 |
nicklas |
655 |
} |
6070 |
20 Nov 20 |
nicklas |
656 |
else |
6070 |
20 Nov 20 |
nicklas |
657 |
{ |
6070 |
20 Nov 20 |
nicklas |
658 |
canvas.moveText(x, y); |
6070 |
20 Nov 20 |
nicklas |
659 |
} |
6069 |
19 Nov 20 |
nicklas |
660 |
canvas.showText(text); |
6069 |
19 Nov 20 |
nicklas |
661 |
canvas.endText(); |
6069 |
19 Nov 20 |
nicklas |
662 |
canvas.restoreState(); |
6092 |
14 Dec 20 |
nicklas |
663 |
return fontSize; |
6069 |
19 Nov 20 |
nicklas |
664 |
} |
6069 |
19 Nov 20 |
nicklas |
665 |
|
6069 |
19 Nov 20 |
nicklas |
666 |
/** |
6058 |
17 Nov 20 |
nicklas |
Draw a circle that is centerd on the x and y coordinates with |
6058 |
17 Nov 20 |
nicklas |
radius r. |
6058 |
17 Nov 20 |
nicklas |
669 |
*/ |
6058 |
17 Nov 20 |
nicklas |
670 |
public void drawCircle(float x, float y, float r, Color fillColor) |
6058 |
17 Nov 20 |
nicklas |
671 |
{ |
6058 |
17 Nov 20 |
nicklas |
672 |
canvas.saveState(); |
6075 |
23 Nov 20 |
nicklas |
673 |
canvas.setLineWidth(0.7f); |
6058 |
17 Nov 20 |
nicklas |
674 |
canvas.setFillColor(fillColor); |
6058 |
17 Nov 20 |
nicklas |
675 |
canvas.circle(x, y, r); |
6058 |
17 Nov 20 |
nicklas |
676 |
canvas.fillStroke(); |
6058 |
17 Nov 20 |
nicklas |
677 |
canvas.restoreState(); |
6058 |
17 Nov 20 |
nicklas |
678 |
} |
6058 |
17 Nov 20 |
nicklas |
679 |
|
6058 |
17 Nov 20 |
nicklas |
680 |
/** |
6058 |
17 Nov 20 |
nicklas |
Import an image into the PDF document and place it at the location specified |
6058 |
17 Nov 20 |
nicklas |
by the x and y coordinates. The image is automatically scaled to fit within the |
6058 |
17 Nov 20 |
nicklas |
given width and height while preserving aspect ratio. It can be aligned either to |
6058 |
17 Nov 20 |
nicklas |
left/right or top/bottom. |
6058 |
17 Nov 20 |
nicklas |
685 |
*/ |
6058 |
17 Nov 20 |
nicklas |
686 |
public void importImage(InputStream image, float x, float y, float width, float height, Align align) |
6058 |
17 Nov 20 |
nicklas |
687 |
throws IOException |
6058 |
17 Nov 20 |
nicklas |
688 |
{ |
6058 |
17 Nov 20 |
nicklas |
689 |
ByteArrayOutputStream bytes = new ByteArrayOutputStream(4096); |
6058 |
17 Nov 20 |
nicklas |
690 |
try |
6058 |
17 Nov 20 |
nicklas |
691 |
{ |
6058 |
17 Nov 20 |
nicklas |
692 |
FileUtil.copy(image, bytes); |
6058 |
17 Nov 20 |
nicklas |
693 |
bytes.close(); |
6060 |
17 Nov 20 |
nicklas |
694 |
byte[] data = bytes.toByteArray(); |
6060 |
17 Nov 20 |
nicklas |
695 |
ImageType imgType = ImageTypeDetector.detectImageType(data); |
6060 |
17 Nov 20 |
nicklas |
696 |
ImageData img = null; |
6060 |
17 Nov 20 |
nicklas |
697 |
WmfImageHelper wmfHelper = null; |
6060 |
17 Nov 20 |
nicklas |
698 |
if (imgType == ImageType.WMF) |
6060 |
17 Nov 20 |
nicklas |
699 |
{ |
6060 |
17 Nov 20 |
nicklas |
700 |
img = new WmfImageData(data); |
6060 |
17 Nov 20 |
nicklas |
701 |
wmfHelper = new WmfImageHelper(img); |
6060 |
17 Nov 20 |
nicklas |
702 |
} |
6060 |
17 Nov 20 |
nicklas |
703 |
else |
6060 |
17 Nov 20 |
nicklas |
704 |
{ |
6060 |
17 Nov 20 |
nicklas |
705 |
img = ImageDataFactory.create(data); |
6060 |
17 Nov 20 |
nicklas |
706 |
} |
6060 |
17 Nov 20 |
nicklas |
707 |
|
6058 |
17 Nov 20 |
nicklas |
708 |
if (Float.isNaN(width) && Float.isNaN(height)) |
6058 |
17 Nov 20 |
nicklas |
709 |
{ |
6058 |
17 Nov 20 |
nicklas |
710 |
width = img.getWidth(); |
6058 |
17 Nov 20 |
nicklas |
711 |
height = img.getHeight(); |
6058 |
17 Nov 20 |
nicklas |
712 |
} |
6058 |
17 Nov 20 |
nicklas |
713 |
else if (Float.isNaN(height)) |
6058 |
17 Nov 20 |
nicklas |
714 |
{ |
6058 |
17 Nov 20 |
nicklas |
715 |
float scale = width / img.getWidth(); |
6058 |
17 Nov 20 |
nicklas |
716 |
height = scale * img.getHeight(); |
6058 |
17 Nov 20 |
nicklas |
717 |
} |
6058 |
17 Nov 20 |
nicklas |
718 |
else if (Float.isNaN(width)) |
6058 |
17 Nov 20 |
nicklas |
719 |
{ |
6058 |
17 Nov 20 |
nicklas |
720 |
float scale = height / img.getHeight(); |
6058 |
17 Nov 20 |
nicklas |
721 |
width = scale * img.getWidth(); |
6058 |
17 Nov 20 |
nicklas |
722 |
} |
6058 |
17 Nov 20 |
nicklas |
723 |
else |
6058 |
17 Nov 20 |
nicklas |
724 |
{ |
6058 |
17 Nov 20 |
nicklas |
725 |
float scale = Math.min(height / img.getHeight(), width / img.getWidth()); |
6058 |
17 Nov 20 |
nicklas |
726 |
height = scale * img.getHeight(); |
6058 |
17 Nov 20 |
nicklas |
727 |
width = scale * img.getWidth(); |
6058 |
17 Nov 20 |
nicklas |
728 |
} |
6058 |
17 Nov 20 |
nicklas |
729 |
if (align != null) |
6058 |
17 Nov 20 |
nicklas |
730 |
{ |
6058 |
17 Nov 20 |
nicklas |
731 |
x = align.adjustX(x, width); |
6058 |
17 Nov 20 |
nicklas |
732 |
y = align.adjustY(y, height); |
6058 |
17 Nov 20 |
nicklas |
733 |
} |
6060 |
17 Nov 20 |
nicklas |
734 |
if (imgType == ImageType.WMF) |
6060 |
17 Nov 20 |
nicklas |
735 |
{ |
6060 |
17 Nov 20 |
nicklas |
736 |
canvas.addXObjectFittedIntoRectangle(wmfHelper.createFormXObject(pdf), new Rectangle(x, y, width, height)); |
6060 |
17 Nov 20 |
nicklas |
737 |
} |
6060 |
17 Nov 20 |
nicklas |
738 |
else |
6060 |
17 Nov 20 |
nicklas |
739 |
{ |
6060 |
17 Nov 20 |
nicklas |
740 |
canvas.addImageFittedIntoRectangle(img, new Rectangle(x, y, width, height), false); |
6060 |
17 Nov 20 |
nicklas |
741 |
} |
6058 |
17 Nov 20 |
nicklas |
742 |
} |
6058 |
17 Nov 20 |
nicklas |
743 |
finally |
6058 |
17 Nov 20 |
nicklas |
744 |
{ |
6058 |
17 Nov 20 |
nicklas |
745 |
FileUtil.close(bytes); |
6058 |
17 Nov 20 |
nicklas |
746 |
FileUtil.close(image); |
6058 |
17 Nov 20 |
nicklas |
747 |
} |
6058 |
17 Nov 20 |
nicklas |
748 |
} |
6058 |
17 Nov 20 |
nicklas |
749 |
|
6064 |
18 Nov 20 |
nicklas |
750 |
/** |
6064 |
18 Nov 20 |
nicklas |
Import an SVG image into the PDF document and place it at the location specified |
6064 |
18 Nov 20 |
nicklas |
by the x and y coordinates. The image is automatically scaled to fit within the |
6064 |
18 Nov 20 |
nicklas |
given width and height while preserving aspect ratio. It can be aligned either to |
6064 |
18 Nov 20 |
nicklas |
left/right or top/bottom. |
6064 |
18 Nov 20 |
nicklas |
755 |
*/ |
6064 |
18 Nov 20 |
nicklas |
756 |
public void importSvg(InputStream svg, float x, float y, float width, float height, Align align) |
6064 |
18 Nov 20 |
nicklas |
757 |
throws IOException |
6064 |
18 Nov 20 |
nicklas |
758 |
{ |
6064 |
18 Nov 20 |
nicklas |
759 |
try |
6064 |
18 Nov 20 |
nicklas |
760 |
{ |
6064 |
18 Nov 20 |
nicklas |
761 |
PdfFormXObject xo = SvgConverter.convertToXObject(svg, pdf); |
6064 |
18 Nov 20 |
nicklas |
762 |
if (Float.isNaN(width) && Float.isNaN(height)) |
6064 |
18 Nov 20 |
nicklas |
763 |
{ |
6064 |
18 Nov 20 |
nicklas |
764 |
width = xo.getWidth(); |
6064 |
18 Nov 20 |
nicklas |
765 |
height = xo.getHeight(); |
6064 |
18 Nov 20 |
nicklas |
766 |
} |
6064 |
18 Nov 20 |
nicklas |
767 |
else if (Float.isNaN(height)) |
6064 |
18 Nov 20 |
nicklas |
768 |
{ |
6064 |
18 Nov 20 |
nicklas |
769 |
float scale = width / xo.getWidth(); |
6064 |
18 Nov 20 |
nicklas |
770 |
height = scale * xo.getHeight(); |
6064 |
18 Nov 20 |
nicklas |
771 |
} |
6064 |
18 Nov 20 |
nicklas |
772 |
else if (Float.isNaN(width)) |
6064 |
18 Nov 20 |
nicklas |
773 |
{ |
6064 |
18 Nov 20 |
nicklas |
774 |
float scale = height / xo.getHeight(); |
6064 |
18 Nov 20 |
nicklas |
775 |
width = scale * xo.getWidth(); |
6064 |
18 Nov 20 |
nicklas |
776 |
} |
6064 |
18 Nov 20 |
nicklas |
777 |
else |
6064 |
18 Nov 20 |
nicklas |
778 |
{ |
6064 |
18 Nov 20 |
nicklas |
779 |
float scale = Math.min(height / xo.getHeight(), width / xo.getWidth()); |
6064 |
18 Nov 20 |
nicklas |
780 |
height = scale * xo.getHeight(); |
6064 |
18 Nov 20 |
nicklas |
781 |
width = scale * xo.getWidth(); |
6064 |
18 Nov 20 |
nicklas |
782 |
} |
6064 |
18 Nov 20 |
nicklas |
783 |
if (align != null) |
6064 |
18 Nov 20 |
nicklas |
784 |
{ |
6064 |
18 Nov 20 |
nicklas |
785 |
x = align.adjustX(x, width); |
6064 |
18 Nov 20 |
nicklas |
786 |
y = align.adjustY(y, height); |
6064 |
18 Nov 20 |
nicklas |
787 |
} |
6064 |
18 Nov 20 |
nicklas |
788 |
canvas.addXObjectFittedIntoRectangle(xo, new Rectangle(x, y, width, height)); |
6064 |
18 Nov 20 |
nicklas |
789 |
} |
6064 |
18 Nov 20 |
nicklas |
790 |
finally |
6064 |
18 Nov 20 |
nicklas |
791 |
{ |
6064 |
18 Nov 20 |
nicklas |
792 |
FileUtil.close(svg); |
6064 |
18 Nov 20 |
nicklas |
793 |
} |
6064 |
18 Nov 20 |
nicklas |
794 |
} |
6064 |
18 Nov 20 |
nicklas |
795 |
|
6081 |
25 Nov 20 |
nicklas |
796 |
/** |
6081 |
25 Nov 20 |
nicklas |
Load a PDF file specified by the given path on the real file system. |
6081 |
25 Nov 20 |
nicklas |
The PdfDocument is cached. Calling this method multiple times with |
6081 |
25 Nov 20 |
nicklas |
the same path will return the same PdfDocument instance. Used by |
6081 |
25 Nov 20 |
nicklas |
{@link #importPdf(TemplateFilePage, float, float, float, float, Align)} |
6081 |
25 Nov 20 |
nicklas |
to avoid having to reload the same PDF more than once even if multiple |
6081 |
25 Nov 20 |
nicklas |
pages are imported. |
6081 |
25 Nov 20 |
nicklas |
803 |
*/ |
6081 |
25 Nov 20 |
nicklas |
804 |
public PdfDocument getDocument(String path) |
6081 |
25 Nov 20 |
nicklas |
805 |
throws IOException |
6081 |
25 Nov 20 |
nicklas |
806 |
{ |
6081 |
25 Nov 20 |
nicklas |
807 |
PdfDocument doc = docCache.get(path); |
6081 |
25 Nov 20 |
nicklas |
808 |
if (doc == null) |
6081 |
25 Nov 20 |
nicklas |
809 |
{ |
6081 |
25 Nov 20 |
nicklas |
810 |
PdfReader reader = new PdfReader(new FileInputStream(path)); |
6081 |
25 Nov 20 |
nicklas |
811 |
doc = new PdfDocument(reader); |
6081 |
25 Nov 20 |
nicklas |
812 |
docCache.put(path, doc); |
6081 |
25 Nov 20 |
nicklas |
813 |
} |
6081 |
25 Nov 20 |
nicklas |
814 |
return doc; |
6081 |
25 Nov 20 |
nicklas |
815 |
} |
6058 |
17 Nov 20 |
nicklas |
816 |
|
6058 |
17 Nov 20 |
nicklas |
817 |
/** |
6058 |
17 Nov 20 |
nicklas |
Import a PDF template document. |
6058 |
17 Nov 20 |
nicklas |
819 |
*/ |
6061 |
18 Nov 20 |
nicklas |
820 |
public void importPdf(TemplateFilePage pdf, float x, float y, float width, float height, Align align) |
6058 |
17 Nov 20 |
nicklas |
821 |
throws IOException |
6058 |
17 Nov 20 |
nicklas |
822 |
{ |
6081 |
25 Nov 20 |
nicklas |
823 |
PdfDocument doc = getDocument(pdf.getPath()); |
6081 |
25 Nov 20 |
nicklas |
824 |
importPdf(doc, pdf.getPage(), x, y, width, height, align); |
6058 |
17 Nov 20 |
nicklas |
825 |
} |
6058 |
17 Nov 20 |
nicklas |
826 |
|
6058 |
17 Nov 20 |
nicklas |
827 |
/** |
6058 |
17 Nov 20 |
nicklas |
Import another PDF document and place it at the location specified |
6058 |
17 Nov 20 |
nicklas |
by the x and y coordinates. |
6058 |
17 Nov 20 |
nicklas |
830 |
*/ |
6061 |
18 Nov 20 |
nicklas |
831 |
public void importPdf(InputStream pdf, int pageNo, float x, float y, float width, float height, Align align) |
6058 |
17 Nov 20 |
nicklas |
832 |
throws IOException |
6058 |
17 Nov 20 |
nicklas |
833 |
{ |
6058 |
17 Nov 20 |
nicklas |
// Load existing PDF |
6081 |
25 Nov 20 |
nicklas |
835 |
PdfReader reader = null; |
6081 |
25 Nov 20 |
nicklas |
836 |
PdfDocument other = null; |
6081 |
25 Nov 20 |
nicklas |
837 |
try |
6081 |
25 Nov 20 |
nicklas |
838 |
{ |
6081 |
25 Nov 20 |
nicklas |
839 |
reader = new PdfReader(pdf); |
6081 |
25 Nov 20 |
nicklas |
840 |
other = new PdfDocument(reader); |
6081 |
25 Nov 20 |
nicklas |
841 |
importPdf(other, pageNo, x, y, width, height, align); |
6081 |
25 Nov 20 |
nicklas |
842 |
} |
6081 |
25 Nov 20 |
nicklas |
843 |
finally |
6081 |
25 Nov 20 |
nicklas |
844 |
{ |
6081 |
25 Nov 20 |
nicklas |
845 |
FileUtil.close(other); |
6081 |
25 Nov 20 |
nicklas |
846 |
FileUtil.close(pdf); |
6081 |
25 Nov 20 |
nicklas |
847 |
} |
6058 |
17 Nov 20 |
nicklas |
848 |
} |
6058 |
17 Nov 20 |
nicklas |
849 |
|
6061 |
18 Nov 20 |
nicklas |
850 |
public void importPdf(PdfDocument from, int pageNo, float x, float y, float width, float height, Align align) |
6058 |
17 Nov 20 |
nicklas |
851 |
{ |
6058 |
17 Nov 20 |
nicklas |
852 |
try |
6058 |
17 Nov 20 |
nicklas |
853 |
{ |
6060 |
17 Nov 20 |
nicklas |
854 |
PdfFormXObject xo = from.getPage(pageNo).copyAsFormXObject(pdf); |
6061 |
18 Nov 20 |
nicklas |
855 |
if (Float.isNaN(width) && Float.isNaN(height)) |
6061 |
18 Nov 20 |
nicklas |
856 |
{ |
6061 |
18 Nov 20 |
nicklas |
857 |
width = xo.getWidth(); |
6061 |
18 Nov 20 |
nicklas |
858 |
height = xo.getHeight(); |
6061 |
18 Nov 20 |
nicklas |
859 |
} |
6061 |
18 Nov 20 |
nicklas |
860 |
else if (Float.isNaN(height)) |
6061 |
18 Nov 20 |
nicklas |
861 |
{ |
6061 |
18 Nov 20 |
nicklas |
862 |
float scale = width / xo.getWidth(); |
6061 |
18 Nov 20 |
nicklas |
863 |
height = scale * xo.getHeight(); |
6061 |
18 Nov 20 |
nicklas |
864 |
} |
6061 |
18 Nov 20 |
nicklas |
865 |
else if (Float.isNaN(width)) |
6061 |
18 Nov 20 |
nicklas |
866 |
{ |
6061 |
18 Nov 20 |
nicklas |
867 |
float scale = height / xo.getHeight(); |
6061 |
18 Nov 20 |
nicklas |
868 |
width = scale * xo.getWidth(); |
6061 |
18 Nov 20 |
nicklas |
869 |
} |
6061 |
18 Nov 20 |
nicklas |
870 |
else |
6061 |
18 Nov 20 |
nicklas |
871 |
{ |
6061 |
18 Nov 20 |
nicklas |
872 |
float scale = Math.min(height / xo.getHeight(), width / xo.getWidth()); |
6061 |
18 Nov 20 |
nicklas |
873 |
height = scale * xo.getHeight(); |
6061 |
18 Nov 20 |
nicklas |
874 |
width = scale * xo.getWidth(); |
6061 |
18 Nov 20 |
nicklas |
875 |
} |
6061 |
18 Nov 20 |
nicklas |
876 |
if (align != null) |
6061 |
18 Nov 20 |
nicklas |
877 |
{ |
6061 |
18 Nov 20 |
nicklas |
878 |
x = align.adjustX(x, width); |
6061 |
18 Nov 20 |
nicklas |
879 |
y = align.adjustY(y, height); |
6061 |
18 Nov 20 |
nicklas |
880 |
} |
6061 |
18 Nov 20 |
nicklas |
881 |
|
6061 |
18 Nov 20 |
nicklas |
882 |
canvas.addXObjectFittedIntoRectangle(xo, new Rectangle(x, y, width, height)); |
6058 |
17 Nov 20 |
nicklas |
883 |
} |
6058 |
17 Nov 20 |
nicklas |
884 |
catch (IOException ex) |
6058 |
17 Nov 20 |
nicklas |
885 |
{ |
6058 |
17 Nov 20 |
nicklas |
886 |
throw new RuntimeException(ex); |
6058 |
17 Nov 20 |
nicklas |
887 |
} |
6058 |
17 Nov 20 |
nicklas |
888 |
} |
6058 |
17 Nov 20 |
nicklas |
889 |
|
6061 |
18 Nov 20 |
nicklas |
890 |
|
6058 |
17 Nov 20 |
nicklas |
891 |
/** |
6061 |
18 Nov 20 |
nicklas |
Create a barcode image for the PDF document and place it at the location specified |
6061 |
18 Nov 20 |
nicklas |
by the x and y coordinates. The image is stretched to fit within the given |
6061 |
18 Nov 20 |
nicklas |
width and height. |
6061 |
18 Nov 20 |
nicklas |
895 |
*/ |
6061 |
18 Nov 20 |
nicklas |
896 |
public void addBarcode(String text, float x, float y, float width, float height, float rotationDegrees) |
6061 |
18 Nov 20 |
nicklas |
897 |
throws IOException |
6061 |
18 Nov 20 |
nicklas |
898 |
{ |
6061 |
18 Nov 20 |
nicklas |
899 |
Barcode128 code128 = new Barcode128(pdf, defaultFont); |
6061 |
18 Nov 20 |
nicklas |
900 |
code128.setBaseline(-1); |
6061 |
18 Nov 20 |
nicklas |
901 |
code128.setFont(null); |
6061 |
18 Nov 20 |
nicklas |
902 |
code128.setSize(12); |
6061 |
18 Nov 20 |
nicklas |
903 |
code128.setCode(text); |
6061 |
18 Nov 20 |
nicklas |
904 |
code128.setCodeType(Barcode128.CODE128); |
6061 |
18 Nov 20 |
nicklas |
905 |
Rectangle rect = code128.getBarcodeSize(); |
6061 |
18 Nov 20 |
nicklas |
906 |
PdfFormXObject xo = code128.createFormXObject(pdf); |
6061 |
18 Nov 20 |
nicklas |
907 |
|
6061 |
18 Nov 20 |
nicklas |
908 |
if (rotationDegrees > 0) |
6061 |
18 Nov 20 |
nicklas |
909 |
{ |
6061 |
18 Nov 20 |
nicklas |
910 |
AffineTransform t = AffineTransform.getRotateInstance(rotationDegrees * Math.PI / 180); |
6061 |
18 Nov 20 |
nicklas |
911 |
t.scale(width/xo.getWidth(), height/xo.getHeight()); |
6061 |
18 Nov 20 |
nicklas |
912 |
canvas.addXObjectWithTransformationMatrix(xo, (float)t.getScaleX(), (float)t.getShearY(), (float)t.getShearX(), (float)t.getScaleY(), x+height, y); |
6061 |
18 Nov 20 |
nicklas |
913 |
} |
6061 |
18 Nov 20 |
nicklas |
914 |
else |
6061 |
18 Nov 20 |
nicklas |
915 |
{ |
6061 |
18 Nov 20 |
nicklas |
916 |
canvas.addXObjectFittedIntoRectangle(xo, new Rectangle(x, y, width, height)); |
6061 |
18 Nov 20 |
nicklas |
917 |
} |
6061 |
18 Nov 20 |
nicklas |
918 |
} |
6061 |
18 Nov 20 |
nicklas |
919 |
|
6061 |
18 Nov 20 |
nicklas |
920 |
/** |
6058 |
17 Nov 20 |
nicklas |
Set the color to use for "cleaning" areas on the template. |
6058 |
17 Nov 20 |
nicklas |
@see #clearRect(float, float, float, float) |
6058 |
17 Nov 20 |
nicklas |
923 |
*/ |
6075 |
23 Nov 20 |
nicklas |
924 |
public void setClearColor(Color color, float opacity) |
6058 |
17 Nov 20 |
nicklas |
925 |
{ |
6058 |
17 Nov 20 |
nicklas |
926 |
this.clearColor = color; |
6075 |
23 Nov 20 |
nicklas |
927 |
this.clearOpacity = opacity; |
6058 |
17 Nov 20 |
nicklas |
928 |
} |
6058 |
17 Nov 20 |
nicklas |
929 |
|
6058 |
17 Nov 20 |
nicklas |
930 |
/** |
6058 |
17 Nov 20 |
nicklas |
Method for drawing a non-transparent white rectangle on the PDF |
6058 |
17 Nov 20 |
nicklas |
in order to hide what is below. Note! This is a helper method for |
6058 |
17 Nov 20 |
nicklas |
development only where templates etc. are not yet finalized. This |
6058 |
17 Nov 20 |
nicklas |
should never be used in production. |
6058 |
17 Nov 20 |
nicklas |
935 |
*/ |
6058 |
17 Nov 20 |
nicklas |
936 |
public void clearRect(float lowerLeftX, float lowerLeftY, float upperRightX, float upperRightY) |
6058 |
17 Nov 20 |
nicklas |
937 |
{ |
6058 |
17 Nov 20 |
nicklas |
938 |
Rectangle rect = new Rectangle(lowerLeftX, lowerLeftY, upperRightX-lowerLeftX, upperRightY-lowerLeftY); |
6058 |
17 Nov 20 |
nicklas |
939 |
canvas.saveState(); |
6058 |
17 Nov 20 |
nicklas |
940 |
canvas.setFillColor(clearColor); |
6075 |
23 Nov 20 |
nicklas |
941 |
if (!Float.isNaN(clearOpacity)) |
6075 |
23 Nov 20 |
nicklas |
942 |
{ |
6075 |
23 Nov 20 |
nicklas |
943 |
canvas.setExtGState(new PdfExtGState().setFillOpacity(clearOpacity)); |
6075 |
23 Nov 20 |
nicklas |
944 |
} |
6058 |
17 Nov 20 |
nicklas |
945 |
canvas.rectangle(rect); |
6058 |
17 Nov 20 |
nicklas |
946 |
canvas.fill(); |
6058 |
17 Nov 20 |
nicklas |
947 |
canvas.restoreState(); |
6058 |
17 Nov 20 |
nicklas |
948 |
} |
6058 |
17 Nov 20 |
nicklas |
949 |
|
6058 |
17 Nov 20 |
nicklas |
950 |
/** |
6092 |
14 Dec 20 |
nicklas |
Redact/erase the given text a page. NOTE! It is not possible to redact information |
6092 |
14 Dec 20 |
nicklas |
at the same time as adding content to the PDF. If this is needed it must be two |
6092 |
14 Dec 20 |
nicklas |
separate operations using a temporary PDF in-between. |
6092 |
14 Dec 20 |
nicklas |
@param pageNo The number of the page to erase the information from. If 0 or |
6092 |
14 Dec 20 |
nicklas |
negative, the text is redacted from all pages |
6092 |
14 Dec 20 |
nicklas |
@since 4.29 |
6092 |
14 Dec 20 |
nicklas |
957 |
*/ |
6092 |
14 Dec 20 |
nicklas |
958 |
public void redactText(int pageNo, String text) |
6092 |
14 Dec 20 |
nicklas |
959 |
{ |
6092 |
14 Dec 20 |
nicklas |
960 |
RegexBasedCleanupStrategy clean = new RegexBasedCleanupStrategy("\\Q"+text+"\\E"); |
6092 |
14 Dec 20 |
nicklas |
961 |
clean.setRedactionColor(redactColor); |
6092 |
14 Dec 20 |
nicklas |
962 |
PdfPage page = pageNo > 0 ? pdf.getPage(pageNo) : null; |
6092 |
14 Dec 20 |
nicklas |
963 |
if (page == null) |
6092 |
14 Dec 20 |
nicklas |
964 |
{ |
6092 |
14 Dec 20 |
nicklas |
965 |
redactLocations.addAll(new PdfAutoSweep(clean).getPdfCleanUpLocations(pdf)); |
6092 |
14 Dec 20 |
nicklas |
966 |
} |
6092 |
14 Dec 20 |
nicklas |
967 |
else |
6092 |
14 Dec 20 |
nicklas |
968 |
{ |
6092 |
14 Dec 20 |
nicklas |
969 |
redactLocations.addAll(new PdfAutoSweep(clean).getPdfCleanUpLocations(page)); |
6092 |
14 Dec 20 |
nicklas |
970 |
} |
6092 |
14 Dec 20 |
nicklas |
971 |
} |
6092 |
14 Dec 20 |
nicklas |
972 |
|
6092 |
14 Dec 20 |
nicklas |
973 |
/** |
6092 |
14 Dec 20 |
nicklas |
Redact/erase information from a page that is located inside the given |
6092 |
14 Dec 20 |
nicklas |
rectangle. NOTE! It is not possible to redact information at the same time as |
6092 |
14 Dec 20 |
nicklas |
adding content to the PDF. If this is needed it must be two separate operations |
6092 |
14 Dec 20 |
nicklas |
using a temporary PDF in-between. |
6092 |
14 Dec 20 |
nicklas |
@param pageNo The number of the page to erase the information from. If 0 or negative, |
6092 |
14 Dec 20 |
nicklas |
the current page is used |
6092 |
14 Dec 20 |
nicklas |
@since 4.29 |
6092 |
14 Dec 20 |
nicklas |
981 |
*/ |
6092 |
14 Dec 20 |
nicklas |
982 |
public void redactRect(int pageNo, float lowerLeftX, float lowerLeftY, float upperRightX, float upperRightY) |
6092 |
14 Dec 20 |
nicklas |
983 |
{ |
6092 |
14 Dec 20 |
nicklas |
984 |
if (pageNo <= 0) pageNo = getCurrentPageNumber(); |
6092 |
14 Dec 20 |
nicklas |
985 |
Rectangle rect = new Rectangle(lowerLeftX, lowerLeftY, upperRightX-lowerLeftX, upperRightY-lowerLeftY); |
6092 |
14 Dec 20 |
nicklas |
986 |
redactLocations.add(new PdfCleanUpLocation(pageNo, rect, redactColor)); |
6092 |
14 Dec 20 |
nicklas |
987 |
} |
6092 |
14 Dec 20 |
nicklas |
988 |
|
6092 |
14 Dec 20 |
nicklas |
989 |
/** |
6058 |
17 Nov 20 |
nicklas |
Draw grid line around the page. Major grid is 50pt, with minor at every 10pt and |
6058 |
17 Nov 20 |
nicklas |
ticks at every 2pt. |
6058 |
17 Nov 20 |
nicklas |
992 |
*/ |
6058 |
17 Nov 20 |
nicklas |
993 |
public void drawGrid() |
6058 |
17 Nov 20 |
nicklas |
994 |
{ |
6058 |
17 Nov 20 |
nicklas |
995 |
float pageHeight = page.getPageSize().getHeight(); |
6058 |
17 Nov 20 |
nicklas |
996 |
float pageWidth = page.getPageSize().getWidth(); |
6058 |
17 Nov 20 |
nicklas |
997 |
|
6058 |
17 Nov 20 |
nicklas |
// Along Y-axis |
6058 |
17 Nov 20 |
nicklas |
999 |
canvas.saveState(); |
6058 |
17 Nov 20 |
nicklas |
1000 |
canvas.setLineWidth(0.25f); |
6058 |
17 Nov 20 |
nicklas |
1001 |
for (int y = 0; y < pageHeight; y += 2) |
6058 |
17 Nov 20 |
nicklas |
1002 |
{ |
6058 |
17 Nov 20 |
nicklas |
1003 |
int lineLength = y % 50 == 0 ? 15 : (y % 10 == 0 ? 10 : 5); |
6058 |
17 Nov 20 |
nicklas |
1004 |
canvas.moveTo(0, y); |
6058 |
17 Nov 20 |
nicklas |
1005 |
canvas.lineTo(lineLength, y); |
6058 |
17 Nov 20 |
nicklas |
1006 |
canvas.moveTo(pageWidth-lineLength, y); |
6058 |
17 Nov 20 |
nicklas |
1007 |
canvas.lineTo(pageWidth, y); |
6058 |
17 Nov 20 |
nicklas |
1008 |
} |
6058 |
17 Nov 20 |
nicklas |
1009 |
|
6058 |
17 Nov 20 |
nicklas |
// Along X axis |
6058 |
17 Nov 20 |
nicklas |
1011 |
for (int x = 0; x < pageWidth; x += 2) |
6058 |
17 Nov 20 |
nicklas |
1012 |
{ |
6058 |
17 Nov 20 |
nicklas |
1013 |
int lineLength = x % 50 == 0 ? 15 : (x % 10 == 0 ? 10 : 5); |
6058 |
17 Nov 20 |
nicklas |
1014 |
canvas.moveTo(x, 0); |
6058 |
17 Nov 20 |
nicklas |
1015 |
canvas.lineTo(x, lineLength); |
6058 |
17 Nov 20 |
nicklas |
1016 |
canvas.moveTo(x, pageHeight-lineLength); |
6058 |
17 Nov 20 |
nicklas |
1017 |
canvas.lineTo(x, pageHeight); |
6058 |
17 Nov 20 |
nicklas |
1018 |
} |
6058 |
17 Nov 20 |
nicklas |
1019 |
|
6058 |
17 Nov 20 |
nicklas |
1020 |
canvas.stroke(); |
6058 |
17 Nov 20 |
nicklas |
1021 |
canvas.restoreState(); |
6058 |
17 Nov 20 |
nicklas |
1022 |
|
6058 |
17 Nov 20 |
nicklas |
1023 |
PdfFont gridFont = getDefaultFont(); |
6058 |
17 Nov 20 |
nicklas |
1024 |
float gridFontSize = 8; |
6058 |
17 Nov 20 |
nicklas |
1025 |
canvas.setFontAndSize(gridFont, gridFontSize); |
6058 |
17 Nov 20 |
nicklas |
1026 |
canvas.beginText(); |
6058 |
17 Nov 20 |
nicklas |
// Put labels on major grid lines |
6058 |
17 Nov 20 |
nicklas |
1028 |
for (int y = 50; y < pageHeight; y += 50) |
6058 |
17 Nov 20 |
nicklas |
1029 |
{ |
6058 |
17 Nov 20 |
nicklas |
1030 |
String lbl = Integer.toString(y); |
6058 |
17 Nov 20 |
nicklas |
1031 |
float lblWidth = gridFont.getWidth(lbl, gridFontSize); |
6058 |
17 Nov 20 |
nicklas |
1032 |
canvas.setTextMatrix(16, y-3); |
6058 |
17 Nov 20 |
nicklas |
1033 |
canvas.showText(lbl); |
6058 |
17 Nov 20 |
nicklas |
1034 |
canvas.setTextMatrix(pageWidth-16-lblWidth, y-3); |
6058 |
17 Nov 20 |
nicklas |
1035 |
canvas.showText(lbl); |
6058 |
17 Nov 20 |
nicklas |
1036 |
} |
6058 |
17 Nov 20 |
nicklas |
1037 |
for (int x = 50; x < pageWidth; x += 50) |
6058 |
17 Nov 20 |
nicklas |
1038 |
{ |
6058 |
17 Nov 20 |
nicklas |
1039 |
String lbl = Integer.toString(x); |
6058 |
17 Nov 20 |
nicklas |
1040 |
float lblWidth = gridFont.getWidth(lbl, gridFontSize); |
6058 |
17 Nov 20 |
nicklas |
1041 |
canvas.setTextMatrix(0, 1, -1, 0, x+3, 16); // 90 degreese rotation |
6058 |
17 Nov 20 |
nicklas |
1042 |
canvas.showText(lbl); |
6058 |
17 Nov 20 |
nicklas |
1043 |
canvas.setTextMatrix(0, 1, -1, 0, x+3, pageHeight-16-lblWidth); |
6058 |
17 Nov 20 |
nicklas |
1044 |
canvas.showText(lbl); |
6058 |
17 Nov 20 |
nicklas |
1045 |
} |
6058 |
17 Nov 20 |
nicklas |
1046 |
canvas.endText(); |
6058 |
17 Nov 20 |
nicklas |
1047 |
} |
6058 |
17 Nov 20 |
nicklas |
1048 |
|
6058 |
17 Nov 20 |
nicklas |
1049 |
/** |
6058 |
17 Nov 20 |
nicklas |
Alignment specifications for text, images, etc. |
6058 |
17 Nov 20 |
nicklas |
1051 |
*/ |
6058 |
17 Nov 20 |
nicklas |
1052 |
public enum Align |
6058 |
17 Nov 20 |
nicklas |
1053 |
{ |
6058 |
17 Nov 20 |
nicklas |
1054 |
/** |
6058 |
17 Nov 20 |
nicklas |
Align the left and bottom side to the specified coordinate. |
6058 |
17 Nov 20 |
nicklas |
1056 |
*/ |
6058 |
17 Nov 20 |
nicklas |
1057 |
LEFT, |
6058 |
17 Nov 20 |
nicklas |
1058 |
|
6058 |
17 Nov 20 |
nicklas |
1059 |
/** |
6058 |
17 Nov 20 |
nicklas |
Align the center (and bottom) to the specified coordinate. |
6058 |
17 Nov 20 |
nicklas |
1061 |
*/ |
6058 |
17 Nov 20 |
nicklas |
1062 |
CENTER |
6058 |
17 Nov 20 |
nicklas |
1063 |
{ |
6058 |
17 Nov 20 |
nicklas |
1064 |
@Override |
6058 |
17 Nov 20 |
nicklas |
1065 |
public float adjustX(float x, PdfFont font, String text, float size) |
6058 |
17 Nov 20 |
nicklas |
1066 |
{ |
6058 |
17 Nov 20 |
nicklas |
1067 |
return x-font.getWidth(text, size)/2; |
6058 |
17 Nov 20 |
nicklas |
1068 |
} |
6058 |
17 Nov 20 |
nicklas |
1069 |
@Override |
6058 |
17 Nov 20 |
nicklas |
1070 |
public float adjustX(float x, float width) |
6058 |
17 Nov 20 |
nicklas |
1071 |
{ |
6058 |
17 Nov 20 |
nicklas |
1072 |
return x-width/2; |
6058 |
17 Nov 20 |
nicklas |
1073 |
} |
6058 |
17 Nov 20 |
nicklas |
1074 |
}, |
6058 |
17 Nov 20 |
nicklas |
1075 |
|
6058 |
17 Nov 20 |
nicklas |
1076 |
/** |
6058 |
17 Nov 20 |
nicklas |
Align the right (and bottom) to the specified coordinate. |
6058 |
17 Nov 20 |
nicklas |
1078 |
*/ |
6058 |
17 Nov 20 |
nicklas |
1079 |
RIGHT |
6058 |
17 Nov 20 |
nicklas |
1080 |
{ |
6058 |
17 Nov 20 |
nicklas |
1081 |
@Override |
6058 |
17 Nov 20 |
nicklas |
1082 |
public float adjustX(float x, PdfFont font, String text, float size) |
6058 |
17 Nov 20 |
nicklas |
1083 |
{ |
6058 |
17 Nov 20 |
nicklas |
1084 |
return x-font.getWidth(text, size); |
6058 |
17 Nov 20 |
nicklas |
1085 |
} |
6058 |
17 Nov 20 |
nicklas |
1086 |
@Override |
6058 |
17 Nov 20 |
nicklas |
1087 |
public float adjustX(float x, float width) |
6058 |
17 Nov 20 |
nicklas |
1088 |
{ |
6058 |
17 Nov 20 |
nicklas |
1089 |
return x-width; |
6058 |
17 Nov 20 |
nicklas |
1090 |
} |
6058 |
17 Nov 20 |
nicklas |
1091 |
}, |
6058 |
17 Nov 20 |
nicklas |
1092 |
|
6058 |
17 Nov 20 |
nicklas |
1093 |
/** |
6058 |
17 Nov 20 |
nicklas |
Align the left and top to the specified coordinate. |
6058 |
17 Nov 20 |
nicklas |
1095 |
*/ |
6058 |
17 Nov 20 |
nicklas |
1096 |
TOP_LEFT |
6058 |
17 Nov 20 |
nicklas |
1097 |
{ |
6058 |
17 Nov 20 |
nicklas |
1098 |
@Override |
6058 |
17 Nov 20 |
nicklas |
1099 |
public float adjustY(float y, float height) |
6058 |
17 Nov 20 |
nicklas |
1100 |
{ |
6058 |
17 Nov 20 |
nicklas |
1101 |
return y-height; |
6058 |
17 Nov 20 |
nicklas |
1102 |
} |
6058 |
17 Nov 20 |
nicklas |
1103 |
|
6058 |
17 Nov 20 |
nicklas |
1104 |
}, |
6058 |
17 Nov 20 |
nicklas |
1105 |
|
6058 |
17 Nov 20 |
nicklas |
1106 |
/** |
6058 |
17 Nov 20 |
nicklas |
Align the right and top to the specified coordinate. |
6058 |
17 Nov 20 |
nicklas |
1108 |
*/ |
6058 |
17 Nov 20 |
nicklas |
1109 |
TOP_RIGHT |
6058 |
17 Nov 20 |
nicklas |
1110 |
{ |
6058 |
17 Nov 20 |
nicklas |
1111 |
@Override |
6058 |
17 Nov 20 |
nicklas |
1112 |
public float adjustX(float x, PdfFont font, String text, float size) |
6058 |
17 Nov 20 |
nicklas |
1113 |
{ |
6058 |
17 Nov 20 |
nicklas |
1114 |
return x-font.getWidth(text, size); |
6058 |
17 Nov 20 |
nicklas |
1115 |
} |
6058 |
17 Nov 20 |
nicklas |
1116 |
@Override |
6058 |
17 Nov 20 |
nicklas |
1117 |
public float adjustX(float x, float width) |
6058 |
17 Nov 20 |
nicklas |
1118 |
{ |
6058 |
17 Nov 20 |
nicklas |
1119 |
return x-width; |
6058 |
17 Nov 20 |
nicklas |
1120 |
} |
6058 |
17 Nov 20 |
nicklas |
1121 |
@Override |
6058 |
17 Nov 20 |
nicklas |
1122 |
public float adjustY(float y, float height) |
6058 |
17 Nov 20 |
nicklas |
1123 |
{ |
6058 |
17 Nov 20 |
nicklas |
1124 |
return y-height; |
6058 |
17 Nov 20 |
nicklas |
1125 |
} |
6058 |
17 Nov 20 |
nicklas |
1126 |
}; |
6058 |
17 Nov 20 |
nicklas |
1127 |
|
6058 |
17 Nov 20 |
nicklas |
1128 |
public float adjustX(float x, PdfFont font, String text, float size) |
6058 |
17 Nov 20 |
nicklas |
1129 |
{ |
6058 |
17 Nov 20 |
nicklas |
1130 |
return x; |
6058 |
17 Nov 20 |
nicklas |
1131 |
} |
6058 |
17 Nov 20 |
nicklas |
1132 |
public float adjustX(float x, float width) |
6058 |
17 Nov 20 |
nicklas |
1133 |
{ |
6058 |
17 Nov 20 |
nicklas |
1134 |
return x; |
6058 |
17 Nov 20 |
nicklas |
1135 |
} |
6058 |
17 Nov 20 |
nicklas |
1136 |
public float adjustY(float y, float height) |
6058 |
17 Nov 20 |
nicklas |
1137 |
{ |
6058 |
17 Nov 20 |
nicklas |
1138 |
return y; |
6058 |
17 Nov 20 |
nicklas |
1139 |
} |
6058 |
17 Nov 20 |
nicklas |
1140 |
} |
6069 |
19 Nov 20 |
nicklas |
1141 |
|
6069 |
19 Nov 20 |
nicklas |
1142 |
/** |
6069 |
19 Nov 20 |
nicklas |
Options for text and images, etc. Note that all options doesn't |
6069 |
19 Nov 20 |
nicklas |
apply in all cases. |
6069 |
19 Nov 20 |
nicklas |
1145 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1146 |
public static class Options |
6069 |
19 Nov 20 |
nicklas |
1147 |
implements Cloneable |
6069 |
19 Nov 20 |
nicklas |
1148 |
{ |
6069 |
19 Nov 20 |
nicklas |
1149 |
/** |
6069 |
19 Nov 20 |
nicklas |
Default options. |
6069 |
19 Nov 20 |
nicklas |
1151 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1152 |
public static final Options DEFAULT = new Options().lock(); |
6069 |
19 Nov 20 |
nicklas |
1153 |
|
6069 |
19 Nov 20 |
nicklas |
1154 |
/** |
6069 |
19 Nov 20 |
nicklas |
Default options, except that objects are aligned to the right. |
6069 |
19 Nov 20 |
nicklas |
1156 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1157 |
public static final Options ALIGN_RIGHT = new Options(Align.RIGHT).lock(); |
6069 |
19 Nov 20 |
nicklas |
1158 |
|
6069 |
19 Nov 20 |
nicklas |
1159 |
Align align = Align.LEFT; |
6069 |
19 Nov 20 |
nicklas |
1160 |
Color color = null; |
6069 |
19 Nov 20 |
nicklas |
1161 |
boolean underline = false; |
6069 |
19 Nov 20 |
nicklas |
1162 |
PdfFont font = null; |
6069 |
19 Nov 20 |
nicklas |
1163 |
boolean bold = false; |
6073 |
20 Nov 20 |
nicklas |
1164 |
boolean italic = false; |
6070 |
20 Nov 20 |
nicklas |
1165 |
float rotation = Float.NaN; |
6078 |
24 Nov 20 |
nicklas |
1166 |
float maxTextWidth = Float.NaN; |
6078 |
24 Nov 20 |
nicklas |
1167 |
float minFontSizeFraction = 0.75f; |
6069 |
19 Nov 20 |
nicklas |
1168 |
|
6069 |
19 Nov 20 |
nicklas |
1169 |
private boolean locked; |
6069 |
19 Nov 20 |
nicklas |
1170 |
|
6069 |
19 Nov 20 |
nicklas |
1171 |
/** |
6069 |
19 Nov 20 |
nicklas |
Create a new options instance with all default settings. |
6069 |
19 Nov 20 |
nicklas |
1173 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1174 |
public Options() |
6069 |
19 Nov 20 |
nicklas |
1175 |
{} |
6069 |
19 Nov 20 |
nicklas |
1176 |
|
6069 |
19 Nov 20 |
nicklas |
1177 |
/** |
6069 |
19 Nov 20 |
nicklas |
Create a new options instance with all default settings, except alignment. |
6069 |
19 Nov 20 |
nicklas |
1179 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1180 |
public Options(Align align) |
6069 |
19 Nov 20 |
nicklas |
1181 |
{ |
6069 |
19 Nov 20 |
nicklas |
1182 |
this.align = align; |
6069 |
19 Nov 20 |
nicklas |
1183 |
} |
6069 |
19 Nov 20 |
nicklas |
1184 |
|
6449 |
20 Oct 21 |
nicklas |
1185 |
private Options checkLocked() |
6069 |
19 Nov 20 |
nicklas |
1186 |
{ |
6449 |
20 Oct 21 |
nicklas |
1187 |
if (locked) |
6449 |
20 Oct 21 |
nicklas |
1188 |
{ |
6449 |
20 Oct 21 |
nicklas |
1189 |
try |
6449 |
20 Oct 21 |
nicklas |
1190 |
{ |
6449 |
20 Oct 21 |
nicklas |
1191 |
return this.clone(); |
6449 |
20 Oct 21 |
nicklas |
1192 |
} |
6449 |
20 Oct 21 |
nicklas |
1193 |
catch (CloneNotSupportedException ex) |
6449 |
20 Oct 21 |
nicklas |
1194 |
{ |
6449 |
20 Oct 21 |
nicklas |
// Should never happen |
6449 |
20 Oct 21 |
nicklas |
1196 |
throw new IllegalStateException("Options is locked"); |
6449 |
20 Oct 21 |
nicklas |
1197 |
} |
6449 |
20 Oct 21 |
nicklas |
1198 |
} |
6449 |
20 Oct 21 |
nicklas |
1199 |
return this; |
6069 |
19 Nov 20 |
nicklas |
1200 |
} |
6069 |
19 Nov 20 |
nicklas |
1201 |
|
6069 |
19 Nov 20 |
nicklas |
1202 |
/** |
6069 |
19 Nov 20 |
nicklas |
Are the options locked? |
6069 |
19 Nov 20 |
nicklas |
1204 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1205 |
public boolean isLocked() |
6069 |
19 Nov 20 |
nicklas |
1206 |
{ |
6069 |
19 Nov 20 |
nicklas |
1207 |
return locked; |
6069 |
19 Nov 20 |
nicklas |
1208 |
} |
6069 |
19 Nov 20 |
nicklas |
1209 |
|
6069 |
19 Nov 20 |
nicklas |
1210 |
/** |
6069 |
19 Nov 20 |
nicklas |
Clone the options and return an unlocked instance. |
6069 |
19 Nov 20 |
nicklas |
1212 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1213 |
@Override |
6449 |
20 Oct 21 |
nicklas |
1214 |
public Options clone() |
6069 |
19 Nov 20 |
nicklas |
1215 |
throws CloneNotSupportedException |
6069 |
19 Nov 20 |
nicklas |
1216 |
{ |
6069 |
19 Nov 20 |
nicklas |
1217 |
Options clone = (Options)super.clone(); |
6069 |
19 Nov 20 |
nicklas |
1218 |
clone.locked = false; |
6069 |
19 Nov 20 |
nicklas |
1219 |
return clone; |
6069 |
19 Nov 20 |
nicklas |
1220 |
} |
6069 |
19 Nov 20 |
nicklas |
1221 |
|
6069 |
19 Nov 20 |
nicklas |
1222 |
/** |
6069 |
19 Nov 20 |
nicklas |
Lock the options for modifications. |
6069 |
19 Nov 20 |
nicklas |
1224 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1225 |
public Options lock() |
6069 |
19 Nov 20 |
nicklas |
1226 |
{ |
6069 |
19 Nov 20 |
nicklas |
1227 |
this.locked = true; |
6069 |
19 Nov 20 |
nicklas |
1228 |
return this; |
6069 |
19 Nov 20 |
nicklas |
1229 |
} |
6069 |
19 Nov 20 |
nicklas |
1230 |
|
6069 |
19 Nov 20 |
nicklas |
1231 |
public Align getAlign() |
6069 |
19 Nov 20 |
nicklas |
1232 |
{ |
6069 |
19 Nov 20 |
nicklas |
1233 |
return align; |
6069 |
19 Nov 20 |
nicklas |
1234 |
} |
6069 |
19 Nov 20 |
nicklas |
1235 |
|
6069 |
19 Nov 20 |
nicklas |
1236 |
/** |
6069 |
19 Nov 20 |
nicklas |
Change alignment. |
6069 |
19 Nov 20 |
nicklas |
1238 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1239 |
public Options align(Align align) |
6069 |
19 Nov 20 |
nicklas |
1240 |
{ |
6449 |
20 Oct 21 |
nicklas |
1241 |
Options o = checkLocked(); |
6449 |
20 Oct 21 |
nicklas |
1242 |
o.align = align; |
6449 |
20 Oct 21 |
nicklas |
1243 |
return o; |
6069 |
19 Nov 20 |
nicklas |
1244 |
} |
6069 |
19 Nov 20 |
nicklas |
1245 |
|
6069 |
19 Nov 20 |
nicklas |
1246 |
public Color getColor() |
6069 |
19 Nov 20 |
nicklas |
1247 |
{ |
6069 |
19 Nov 20 |
nicklas |
1248 |
return color; |
6069 |
19 Nov 20 |
nicklas |
1249 |
} |
6069 |
19 Nov 20 |
nicklas |
1250 |
|
6069 |
19 Nov 20 |
nicklas |
1251 |
/** |
6069 |
19 Nov 20 |
nicklas |
Change text color. |
6069 |
19 Nov 20 |
nicklas |
1253 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1254 |
public Options color(Color color) |
6069 |
19 Nov 20 |
nicklas |
1255 |
{ |
6449 |
20 Oct 21 |
nicklas |
1256 |
Options o = checkLocked(); |
6449 |
20 Oct 21 |
nicklas |
1257 |
o.color = color; |
6449 |
20 Oct 21 |
nicklas |
1258 |
return o; |
6069 |
19 Nov 20 |
nicklas |
1259 |
} |
6069 |
19 Nov 20 |
nicklas |
1260 |
|
6069 |
19 Nov 20 |
nicklas |
1261 |
public boolean getUnderline() |
6069 |
19 Nov 20 |
nicklas |
1262 |
{ |
6069 |
19 Nov 20 |
nicklas |
1263 |
return underline; |
6069 |
19 Nov 20 |
nicklas |
1264 |
} |
6069 |
19 Nov 20 |
nicklas |
1265 |
|
6069 |
19 Nov 20 |
nicklas |
1266 |
/** |
6069 |
19 Nov 20 |
nicklas |
Enable underlined text. |
6069 |
19 Nov 20 |
nicklas |
1268 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1269 |
public Options underline() |
6069 |
19 Nov 20 |
nicklas |
1270 |
{ |
6069 |
19 Nov 20 |
nicklas |
1271 |
return underline(true); |
6069 |
19 Nov 20 |
nicklas |
1272 |
} |
6069 |
19 Nov 20 |
nicklas |
1273 |
|
6069 |
19 Nov 20 |
nicklas |
1274 |
/** |
6069 |
19 Nov 20 |
nicklas |
Enable or disable underlined text. |
6069 |
19 Nov 20 |
nicklas |
1276 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1277 |
public Options underline(boolean underline) |
6069 |
19 Nov 20 |
nicklas |
1278 |
{ |
6449 |
20 Oct 21 |
nicklas |
1279 |
Options o = checkLocked(); |
6449 |
20 Oct 21 |
nicklas |
1280 |
o.underline = underline; |
6449 |
20 Oct 21 |
nicklas |
1281 |
return o; |
6069 |
19 Nov 20 |
nicklas |
1282 |
} |
6069 |
19 Nov 20 |
nicklas |
1283 |
|
6069 |
19 Nov 20 |
nicklas |
1284 |
public PdfFont getFont() |
6069 |
19 Nov 20 |
nicklas |
1285 |
{ |
6069 |
19 Nov 20 |
nicklas |
1286 |
return font; |
6069 |
19 Nov 20 |
nicklas |
1287 |
} |
6069 |
19 Nov 20 |
nicklas |
1288 |
|
6069 |
19 Nov 20 |
nicklas |
1289 |
/** |
6069 |
19 Nov 20 |
nicklas |
Change text font. |
6069 |
19 Nov 20 |
nicklas |
1291 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1292 |
public Options font(PdfFont font) |
6069 |
19 Nov 20 |
nicklas |
1293 |
{ |
6449 |
20 Oct 21 |
nicklas |
1294 |
Options o = checkLocked(); |
6449 |
20 Oct 21 |
nicklas |
1295 |
o.font = font; |
6073 |
20 Nov 20 |
nicklas |
1296 |
if (font != null) |
6073 |
20 Nov 20 |
nicklas |
1297 |
{ |
6073 |
20 Nov 20 |
nicklas |
1298 |
FontNames fn = font.getFontProgram().getFontNames(); |
6449 |
20 Oct 21 |
nicklas |
1299 |
o.bold = fn.isBold(); |
6449 |
20 Oct 21 |
nicklas |
1300 |
o.italic = fn.isItalic(); |
6073 |
20 Nov 20 |
nicklas |
1301 |
} |
6449 |
20 Oct 21 |
nicklas |
1302 |
return o; |
6069 |
19 Nov 20 |
nicklas |
1303 |
} |
6069 |
19 Nov 20 |
nicklas |
1304 |
|
6069 |
19 Nov 20 |
nicklas |
1305 |
public boolean getBold() |
6069 |
19 Nov 20 |
nicklas |
1306 |
{ |
6069 |
19 Nov 20 |
nicklas |
1307 |
return bold; |
6069 |
19 Nov 20 |
nicklas |
1308 |
} |
6069 |
19 Nov 20 |
nicklas |
1309 |
|
6069 |
19 Nov 20 |
nicklas |
1310 |
/** |
6069 |
19 Nov 20 |
nicklas |
Enable bold text. This flag is automatically updated when a font is |
6069 |
19 Nov 20 |
nicklas |
specified by {@link #font(PdfFont)}. If no explicit font has been set |
6069 |
19 Nov 20 |
nicklas |
and this flag is enabled the default bold font is selected. |
6069 |
19 Nov 20 |
nicklas |
1314 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1315 |
public Options bold() |
6069 |
19 Nov 20 |
nicklas |
1316 |
{ |
6069 |
19 Nov 20 |
nicklas |
1317 |
return bold(true); |
6069 |
19 Nov 20 |
nicklas |
1318 |
} |
6069 |
19 Nov 20 |
nicklas |
1319 |
|
6069 |
19 Nov 20 |
nicklas |
1320 |
/** |
6069 |
19 Nov 20 |
nicklas |
Enable or disable bold text. |
6069 |
19 Nov 20 |
nicklas |
1322 |
*/ |
6069 |
19 Nov 20 |
nicklas |
1323 |
public Options bold(boolean bold) |
6069 |
19 Nov 20 |
nicklas |
1324 |
{ |
6449 |
20 Oct 21 |
nicklas |
1325 |
Options o = checkLocked(); |
6449 |
20 Oct 21 |
nicklas |
1326 |
o.bold = bold; |
6449 |
20 Oct 21 |
nicklas |
1327 |
return o; |
6070 |
20 Nov 20 |
nicklas |
1328 |
} |
6070 |
20 Nov 20 |
nicklas |
1329 |
|
6073 |
20 Nov 20 |
nicklas |
1330 |
public boolean getItalic() |
6073 |
20 Nov 20 |
nicklas |
1331 |
{ |
6073 |
20 Nov 20 |
nicklas |
1332 |
return italic; |
6073 |
20 Nov 20 |
nicklas |
1333 |
} |
6073 |
20 Nov 20 |
nicklas |
1334 |
|
6073 |
20 Nov 20 |
nicklas |
1335 |
/** |
6073 |
20 Nov 20 |
nicklas |
Enable italic text. This flag is automatically updated when a font is |
6073 |
20 Nov 20 |
nicklas |
specified by {@link #font(PdfFont)}. If no explicit font has been set |
6073 |
20 Nov 20 |
nicklas |
and this flag is enabled the default italic font is selected. |
6073 |
20 Nov 20 |
nicklas |
1339 |
*/ |
6073 |
20 Nov 20 |
nicklas |
1340 |
public Options italic() |
6073 |
20 Nov 20 |
nicklas |
1341 |
{ |
6073 |
20 Nov 20 |
nicklas |
1342 |
return italic(true); |
6073 |
20 Nov 20 |
nicklas |
1343 |
} |
6073 |
20 Nov 20 |
nicklas |
1344 |
|
6073 |
20 Nov 20 |
nicklas |
1345 |
/** |
6073 |
20 Nov 20 |
nicklas |
Enable or disable italic text. |
6073 |
20 Nov 20 |
nicklas |
1347 |
*/ |
6073 |
20 Nov 20 |
nicklas |
1348 |
public Options italic(boolean italic) |
6073 |
20 Nov 20 |
nicklas |
1349 |
{ |
6449 |
20 Oct 21 |
nicklas |
1350 |
Options o = checkLocked(); |
6449 |
20 Oct 21 |
nicklas |
1351 |
o.italic = italic; |
6449 |
20 Oct 21 |
nicklas |
1352 |
return o; |
6073 |
20 Nov 20 |
nicklas |
1353 |
} |
6073 |
20 Nov 20 |
nicklas |
1354 |
|
6070 |
20 Nov 20 |
nicklas |
1355 |
public float getRotation() |
6070 |
20 Nov 20 |
nicklas |
1356 |
{ |
6070 |
20 Nov 20 |
nicklas |
1357 |
return rotation; |
6070 |
20 Nov 20 |
nicklas |
1358 |
} |
6070 |
20 Nov 20 |
nicklas |
1359 |
|
6070 |
20 Nov 20 |
nicklas |
1360 |
/** |
6070 |
20 Nov 20 |
nicklas |
Rotate text by the specified angle in degrees. |
6070 |
20 Nov 20 |
nicklas |
1362 |
*/ |
6070 |
20 Nov 20 |
nicklas |
1363 |
public Options rotation(float rotation) |
6070 |
20 Nov 20 |
nicklas |
1364 |
{ |
6449 |
20 Oct 21 |
nicklas |
1365 |
Options o = checkLocked(); |
6449 |
20 Oct 21 |
nicklas |
1366 |
o.rotation = rotation; |
6449 |
20 Oct 21 |
nicklas |
1367 |
return o; |
6069 |
19 Nov 20 |
nicklas |
1368 |
} |
6069 |
19 Nov 20 |
nicklas |
1369 |
|
6078 |
24 Nov 20 |
nicklas |
1370 |
public float getMaxTextWidth() |
6078 |
24 Nov 20 |
nicklas |
1371 |
{ |
6078 |
24 Nov 20 |
nicklas |
1372 |
return maxTextWidth; |
6078 |
24 Nov 20 |
nicklas |
1373 |
} |
6078 |
24 Nov 20 |
nicklas |
1374 |
|
6078 |
24 Nov 20 |
nicklas |
1375 |
/** |
6078 |
24 Nov 20 |
nicklas |
Set the max width of the text. If the text |
6078 |
24 Nov 20 |
nicklas |
takes up more space, the font size will automatically |
6078 |
24 Nov 20 |
nicklas |
be decreased in steps of 0.5 points down to 'minFontSizeFraction' |
6078 |
24 Nov 20 |
nicklas |
(default is 0.75) of the original font size. If the text still doesn't |
6078 |
24 Nov 20 |
nicklas |
fit, characters are removed from the end until it is short enough and |
6078 |
24 Nov 20 |
nicklas |
an ellipsis is added. |
6078 |
24 Nov 20 |
nicklas |
1382 |
*/ |
6078 |
24 Nov 20 |
nicklas |
1383 |
public Options maxTextWidth(float maxTextWidth) |
6078 |
24 Nov 20 |
nicklas |
1384 |
{ |
6449 |
20 Oct 21 |
nicklas |
1385 |
Options o = checkLocked(); |
6449 |
20 Oct 21 |
nicklas |
1386 |
o.maxTextWidth = maxTextWidth; |
6449 |
20 Oct 21 |
nicklas |
1387 |
return o; |
6078 |
24 Nov 20 |
nicklas |
1388 |
} |
6078 |
24 Nov 20 |
nicklas |
1389 |
|
6078 |
24 Nov 20 |
nicklas |
1390 |
public float getMinFontSizeFraction() |
6078 |
24 Nov 20 |
nicklas |
1391 |
{ |
6078 |
24 Nov 20 |
nicklas |
1392 |
return minFontSizeFraction; |
6078 |
24 Nov 20 |
nicklas |
1393 |
} |
6078 |
24 Nov 20 |
nicklas |
1394 |
|
6078 |
24 Nov 20 |
nicklas |
1395 |
/** |
6078 |
24 Nov 20 |
nicklas |
Set the lower limit of the font size that is acceptable if |
6078 |
24 Nov 20 |
nicklas |
the text is using more space than specified by 'maxTextWidth'. |
6078 |
24 Nov 20 |
nicklas |
This font size is calculated as a fraction of the original |
6078 |
24 Nov 20 |
nicklas |
font size. The default value is 0.75. |
6078 |
24 Nov 20 |
nicklas |
1400 |
*/ |
6078 |
24 Nov 20 |
nicklas |
1401 |
public Options minFontSizeFraction(float minFontSizeFraction) |
6078 |
24 Nov 20 |
nicklas |
1402 |
{ |
6449 |
20 Oct 21 |
nicklas |
1403 |
Options o = checkLocked(); |
6449 |
20 Oct 21 |
nicklas |
1404 |
o.minFontSizeFraction = minFontSizeFraction; |
6449 |
20 Oct 21 |
nicklas |
1405 |
return o; |
6078 |
24 Nov 20 |
nicklas |
1406 |
} |
6078 |
24 Nov 20 |
nicklas |
1407 |
|
6078 |
24 Nov 20 |
nicklas |
1408 |
|
6069 |
19 Nov 20 |
nicklas |
1409 |
} |
6069 |
19 Nov 20 |
nicklas |
1410 |
|
6058 |
17 Nov 20 |
nicklas |
1411 |
} |