New upstream version 2.0.19
Markus Koschany
4 years ago
0 | Release Notes -- Apache PDFBox -- Version 2.0.18 | |
0 | Release Notes -- Apache PDFBox -- Version 2.0.19 | |
1 | 1 | |
2 | 2 | Introduction |
3 | 3 | ------------ |
4 | 4 | |
5 | 5 | The Apache PDFBox library is an open source Java tool for working with PDF documents. |
6 | 6 | |
7 | This is an incremental bugfix release based on the earlier 2.0.17 release. It contains | |
7 | This is an incremental bugfix release based on the earlier 2.0.18 release. It contains | |
8 | 8 | a couple of fixes and small improvements. |
9 | 9 | |
10 | 10 | For more details on these changes and all the other fixes and improvements |
13 | 13 | |
14 | 14 | Bug |
15 | 15 | |
16 | [PDFBOX-4654] - PDFToImage shows reader image formats in usage | |
17 | [PDFBOX-4655] - ImageIOUtil.WriteImage creates huge PNG images when used with default quality on jdk11 | |
18 | [PDFBOX-4659] - PDFBOX-3531 has re-appeared when trying to use "sun.java2d.cmm.kcms.KcmsServiceProvider" in JAVA 1.8_222 | |
19 | [PDFBOX-4661] - Regression No Unicode mapping with Identity-H font | |
20 | [PDFBOX-4662] - ClassCastException: org.bouncycastle.asn1.DLTaggedObject cannot be cast to org.bouncycastle.asn1.DERTaggedObject | |
21 | [PDFBOX-4665] - PDImageXObject createFromFileByExtension does not close FileInputStream in event of error | |
22 | [PDFBOX-4666] - StackOverflowError with PDFTextStripper.getText() | |
23 | [PDFBOX-4667] - Issue in FontMapperImpl#isCharSetMatch when font codePageRange is -1 | |
24 | [PDFBOX-4672] - Draws the attachment image to the PDF document, and the image displays as a black block | |
25 | [PDFBOX-4674] - PDF Page Render Background Image has Gray Smudges | |
26 | [PDFBOX-4678] - Use PDFontFactory. CreateDefaultFont font to render text, the exported PDF document has a mistake | |
27 | [PDFBOX-4683] - Could not find referenced cmap stream Adobe-Japan1-7 | |
28 | [PDFBOX-4687] - "Iterator.next()" methods should throw "NoSuchElementException" | |
29 | [PDFBOX-4688] - "BigDecimal(double)" should not be used | |
30 | [PDFBOX-4693] - PDF documents with rotated form field(90° degrees in my case) that apply form flattening appear squ | |
31 | [PDFBOX-4696] - Endless loop in OCSP certificate check | |
32 | [PDFBOX-4701] - TextPosition.equal() fails after getDir() | |
33 | [PDFBOX-4706] - support /UserUnit | |
34 | [PDFBOX-4711] - java.lang.ClassCastException: org.apache.pdfbox.cos.COSDictionary cannot be cast to org.apache.pdfbox.cos.COSStream | |
35 | [PDFBOX-4712] - Appearance dictionary should not be empty | |
36 | [PDFBOX-4713] - /AS is required if /AP contains a subdictionary | |
37 | [PDFBOX-4716] - ipag00303.php does not exist, mvn clean install! | |
16 | [PDFBOX-4720] - cmap entries "<0000> <FFFF> <0000>" are cut | |
17 | [PDFBOX-4722] - TestTextStripper doesn't detect when less output | |
18 | [PDFBOX-4724] - Wrong calculation of position in InputStreamSource#readFully | |
19 | [PDFBOX-4727] - ExtractEmbeddedFiles.java example uses name tree keys as file names | |
20 | [PDFBOX-4730] - /OC in form and image XObjects not handled | |
21 | [PDFBOX-4738] - getDocument().getObjects() returns nothing for split result documents | |
22 | [PDFBOX-4741] - NullPointerException in PlainText constructor | |
23 | [PDFBOX-4742] - Incorrect handling of float Infinity and NaN | |
24 | [PDFBOX-4745] - COSObjectKey.hashCode doesn't work for generation numbers > 0 | |
25 | [PDFBOX-4750] - java.io.IOException: Error:Unknown type in content stream:COSNull{} | |
26 | [PDFBOX-4753] - NumberFormatException while parsing a certain PDF document | |
27 | [PDFBOX-4755] - Fonts improperly rendered | |
28 | [PDFBOX-4756] - ScratchFileBuffer seek beyond the last page | |
29 | [PDFBOX-4760] - wordSeparator not being inserted when word ends with " " | |
30 | [PDFBOX-4763] - Can't get inline image raw data | |
31 | [PDFBOX-4765] - NPE in ExtractImages.ImageGraphicsEngine().run() | |
32 | [PDFBOX-4771] - JPEG image with transparency can't be extracted | |
33 | [PDFBOX-4772] - Improve memory consumption of PDAbstractAppearanceHandler (2) | |
34 | [PDFBOX-4777] - Avoid OOM for malformed PDFs using a huge First valkue within object streams | |
38 | 35 | |
39 | 36 | New Feature |
40 | 37 | |
41 | [PDFBOX-4682] - NPE at PDSimpleFont.isStandard14() | |
38 | [PDFBOX-4721] - Move Apache PDFBox from a low-API model | |
42 | 39 | |
43 | 40 | Improvement |
44 | 41 | |
45 | [PDFBOX-4341] - [Patch] PNGConverter: PNG bytes to PDImageXObject converter | |
46 | [PDFBOX-4653] - Clarify what font is missing when not in resources | |
47 | [PDFBOX-4657] - Use smooth interpolation for stencils too | |
48 | [PDFBOX-4685] - Add example of usage of separation color (spot color) | |
49 | [PDFBOX-4686] - Add text to AcroForm examples | |
50 | [PDFBOX-4689] - Invisible Signature should have empty Appearance Dictionary | |
51 | [PDFBOX-4691] - JPEGFactory should not decode JPEG files when only meta data is needed | |
52 | [PDFBOX-4694] - LockedContents flag | |
53 | [PDFBOX-4695] - PDPropBuildDataDict missing setNonEFontNoWarn | |
54 | [PDFBOX-4710] - allow precise RGB stroke colors with setStrokingColor | |
55 | ||
56 | Wish | |
57 | ||
58 | [PDFBOX-4699] - ERROR FeatureRecord array not alphabetically sorted by FeatureTag | |
42 | [PDFBOX-4734] - ExtractImages should create CCITT G4 compressed TIFF files when possible | |
43 | [PDFBOX-4735] - WriteDecodedDoc should create an xref table instead of an xref stream | |
44 | [PDFBOX-4762] - Inconsistent handling of incorrect data | |
45 | [PDFBOX-4766] - PDInlineImage.getSuffix() returns null | |
46 | [PDFBOX-4779] - PDFBOX: Update Bouncy Castle Crypto to version 1.64 | |
59 | 47 | |
60 | 48 | Task |
61 | 49 | |
62 | [PDFBOX-4708] - Add PDAcroFormFlattenTest to 2.0 branch | |
63 | [PDFBOX-4715] - Need to add release version for maven-compiler-plugin | |
50 | [PDFBOX-4757] - Enable as much PDAcroFormFlattenTest tests as possible | |
51 | [PDFBOX-4759] - Add tests for PDFBOX-4153 and PDFBOX-4490 | |
64 | 52 | |
65 | 53 | Sub-task |
66 | 54 | |
67 | [PDFBOX-4703] - Support acroform /Ff flag for signature fields | |
55 | [PDFBOX-4731] - Support RenderDestination | |
56 | [PDFBOX-4732] - Support ImageType | |
68 | 57 | |
69 | 58 | Release Contents |
70 | 59 | ---------------- |
22 | 22 | <parent> |
23 | 23 | <groupId>org.apache.pdfbox</groupId> |
24 | 24 | <artifactId>pdfbox-parent</artifactId> |
25 | <version>2.0.18</version> | |
25 | <version>2.0.19</version> | |
26 | 26 | <relativePath>../parent/pom.xml</relativePath> |
27 | 27 | </parent> |
28 | 28 |
22 | 22 | <parent> |
23 | 23 | <groupId>org.apache.pdfbox</groupId> |
24 | 24 | <artifactId>pdfbox-parent</artifactId> |
25 | <version>2.0.18</version> | |
25 | <version>2.0.19</version> | |
26 | 26 | <relativePath>../parent/pom.xml</relativePath> |
27 | 27 | </parent> |
28 | 28 |
95 | 95 | import org.apache.pdfbox.debugger.ui.ErrorDialog; |
96 | 96 | import org.apache.pdfbox.debugger.ui.ExtensionFileFilter; |
97 | 97 | import org.apache.pdfbox.debugger.ui.FileOpenSaveDialog; |
98 | import org.apache.pdfbox.debugger.ui.ImageTypeMenu; | |
98 | 99 | import org.apache.pdfbox.debugger.ui.LogDialog; |
99 | 100 | import org.apache.pdfbox.debugger.ui.MapEntry; |
100 | 101 | import org.apache.pdfbox.debugger.ui.OSXAdapter; |
103 | 104 | import org.apache.pdfbox.debugger.ui.PageEntry; |
104 | 105 | import org.apache.pdfbox.debugger.ui.ReaderBottomPanel; |
105 | 106 | import org.apache.pdfbox.debugger.ui.RecentFiles; |
107 | import org.apache.pdfbox.debugger.ui.RenderDestinationMenu; | |
106 | 108 | import org.apache.pdfbox.debugger.ui.RotationMenu; |
107 | 109 | import org.apache.pdfbox.debugger.ui.Tree; |
108 | 110 | import org.apache.pdfbox.debugger.ui.WindowPrefs; |
522 | 524 | RotationMenu rotationMenu = RotationMenu.getInstance(); |
523 | 525 | rotationMenu.setEnableMenu(false); |
524 | 526 | viewMenu.add(rotationMenu.getMenu()); |
527 | ||
528 | ImageTypeMenu imageTypeMenu = ImageTypeMenu.getInstance(); | |
529 | imageTypeMenu.setEnableMenu(false); | |
530 | viewMenu.add(imageTypeMenu.getMenu()); | |
531 | ||
532 | RenderDestinationMenu renderDestinationMenu = RenderDestinationMenu.getInstance(); | |
533 | renderDestinationMenu.setEnableMenu(false); | |
534 | viewMenu.add(renderDestinationMenu.getMenu()); | |
525 | 535 | |
526 | 536 | viewMenu.addSeparator(); |
527 | 537 | |
851 | 861 | * Show a Panel describing color spaces in more detail and interactive way. |
852 | 862 | * @param csNode the special color space containing node. |
853 | 863 | */ |
854 | private void showColorPane(Object csNode) | |
864 | private void showColorPane(Object csNode) throws IOException | |
855 | 865 | { |
856 | 866 | csNode = getUnderneathObject(csNode); |
857 | 867 | |
1354 | 1364 | DocumentEntry documentEntry = new DocumentEntry(document, file.getName()); |
1355 | 1365 | ZoomMenu.getInstance().resetZoom(); |
1356 | 1366 | RotationMenu.getInstance().setRotationSelection(RotationMenu.ROTATE_0_DEGREES); |
1367 | ImageTypeMenu.getInstance().setImageTypeSelection(ImageTypeMenu.IMAGETYPE_RGB); | |
1368 | RenderDestinationMenu.getInstance().setRenderDestinationSelection(RenderDestinationMenu.RENDER_DESTINATION_EXPORT); | |
1357 | 1369 | tree.setModel(new PDFTreeModel(documentEntry)); |
1358 | 1370 | // Root/Pages/Kids/[0] is not always the first page, so use the first row instead: |
1359 | 1371 | tree.setSelectionPath(tree.getPathForRow(1)); |
30 | 30 | import org.apache.pdfbox.cos.COSArray; |
31 | 31 | import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceN; |
32 | 32 | |
33 | ||
34 | /** | |
35 | *@author Khyrul Bashar. | |
36 | */ | |
37 | ||
38 | 33 | /** |
39 | 34 | * A class that provides the necessary UI and functionalities to show the DeviceN color space. |
35 | * | |
36 | * @author Khyrul Bashar. | |
37 | * | |
40 | 38 | */ |
41 | 39 | public class CSDeviceN |
42 | 40 | { |
43 | private PDDeviceN deviceN; | |
41 | private final PDDeviceN deviceN; | |
44 | 42 | private JPanel panel; |
45 | 43 | |
46 | 44 | /** |
47 | 45 | * Constructor |
48 | 46 | * |
49 | * @param array COSArray instance that holds DeviceN color space | |
47 | * @param array COSArray instance that holds the DeviceN color space | |
50 | 48 | */ |
51 | public CSDeviceN(COSArray array) | |
49 | public CSDeviceN(COSArray array) throws IOException | |
52 | 50 | { |
53 | try | |
54 | { | |
55 | deviceN = new PDDeviceN(array); | |
56 | DeviceNColorant[] colorants = getColorantData(); | |
57 | initUI(colorants); | |
58 | } | |
59 | catch (IOException e) | |
60 | { | |
61 | throw new RuntimeException(e); | |
62 | } | |
51 | deviceN = new PDDeviceN(array); | |
52 | DeviceNColorant[] colorants = getColorantData(); | |
53 | initUI(colorants); | |
63 | 54 | } |
64 | 55 | |
65 | 56 | /** |
33 | 33 | import org.apache.pdfbox.pdmodel.graphics.color.PDIndexed; |
34 | 34 | |
35 | 35 | /** |
36 | * A class that provides the necessary UI and functionalities to show the Indexed colorspace. | |
37 | * | |
36 | 38 | * @author Khyrul Bashar. |
37 | */ | |
38 | ||
39 | /** | |
40 | * A class that provides the necessary UI and functionalities to show the Indexed colorspace. | |
41 | 39 | */ |
42 | 40 | public class CSIndexed |
43 | 41 | { |
44 | private PDIndexed indexed; | |
42 | private final PDIndexed indexed; | |
45 | 43 | private JPanel panel; |
46 | private int colorCount; | |
44 | private final int colorCount; | |
47 | 45 | |
48 | 46 | /** |
49 | 47 | * Constructor. |
50 | * @param array COSArray instance for Indexed Colorspace. | |
48 | * @param array COSArray instance for Indexed color space. | |
49 | * @throws java.io.IOException | |
51 | 50 | */ |
52 | public CSIndexed(COSArray array) | |
51 | public CSIndexed(COSArray array) throws IOException | |
53 | 52 | { |
54 | try | |
55 | { | |
56 | indexed = new PDIndexed(array); | |
57 | colorCount = getHival(array) + 1; | |
58 | initUI(getColorantData()); | |
59 | } | |
60 | catch (IOException e) | |
61 | { | |
62 | throw new RuntimeException(e); | |
63 | } | |
53 | indexed = new PDIndexed(array); | |
54 | colorCount = getHival(array) + 1; | |
55 | initUI(getColorantData()); | |
64 | 56 | } |
65 | 57 | |
66 | 58 | /** |
39 | 39 | import org.apache.pdfbox.pdmodel.graphics.color.PDSeparation; |
40 | 40 | |
41 | 41 | /** |
42 | * A class that provides the necessary UI and functionalities to show the Separation color space. | |
43 | * | |
42 | 44 | * @author Khyrul Bashar. |
43 | */ | |
44 | ||
45 | /** | |
46 | *A class that provides the necessary UI and functionalities to show the Separation color space. | |
47 | 45 | */ |
48 | 46 | public class CSSeparation implements ChangeListener, ActionListener |
49 | 47 | { |
52 | 50 | private JLabel colorBar; |
53 | 51 | private JPanel panel; |
54 | 52 | |
55 | private PDSeparation separation; | |
53 | private final PDSeparation separation; | |
56 | 54 | private float tintValue = 1; |
57 | 55 | |
58 | 56 | /** |
59 | 57 | * Constructor |
60 | * @param array COSArray instance of the separation color space. | |
61 | */ | |
62 | public CSSeparation(COSArray array) | |
63 | { | |
64 | try | |
65 | { | |
66 | separation = new PDSeparation(array); | |
67 | } | |
68 | catch (IOException e) | |
69 | { | |
70 | throw new RuntimeException(e); | |
71 | } | |
58 | * | |
59 | * @param array COSArray instance of the Separation color space. | |
60 | * | |
61 | * @throws java.io.IOException | |
62 | */ | |
63 | public CSSeparation(COSArray array) throws IOException | |
64 | { | |
65 | separation = new PDSeparation(array); | |
72 | 66 | initUI(); |
73 | 67 | initValues(); |
74 | 68 | } |
76 | 70 | /** |
77 | 71 | * initialize all the UI elements and arrange them. |
78 | 72 | */ |
79 | private void initUI() | |
73 | private void initUI() throws IOException | |
80 | 74 | { |
81 | 75 | Font boldFont = new Font(Font.MONOSPACED, Font.BOLD, 20); |
82 | 76 | |
204 | 198 | @Override |
205 | 199 | public void stateChanged(ChangeEvent changeEvent) |
206 | 200 | { |
207 | int value = slider.getValue(); | |
208 | tintValue = getFloatRepresentation(value); | |
209 | tintField.setText(Float.toString(tintValue)); | |
201 | int value = slider.getValue(); | |
202 | tintValue = getFloatRepresentation(value); | |
203 | tintField.setText(Float.toString(tintValue)); | |
204 | try | |
205 | { | |
210 | 206 | updateColorBar(); |
207 | } | |
208 | catch (IOException ex) | |
209 | { | |
210 | tintField.setText(ex.getMessage()); | |
211 | } | |
211 | 212 | } |
212 | 213 | |
213 | 214 | /** |
228 | 229 | { |
229 | 230 | tintField.setText(Float.toString(tintValue)); |
230 | 231 | } |
231 | } | |
232 | ||
233 | private void updateColorBar() | |
234 | { | |
235 | try | |
236 | { | |
237 | float[] rgbValues = separation.toRGB(new float[] {tintValue}); | |
238 | colorBar.setBackground(new Color(rgbValues[0], rgbValues[1], rgbValues[2])); | |
239 | } | |
240 | catch (IOException e) | |
241 | { | |
242 | throw new RuntimeException(e); | |
243 | } | |
232 | catch (IOException ex) | |
233 | { | |
234 | tintField.setText(ex.getMessage()); | |
235 | } | |
236 | } | |
237 | ||
238 | private void updateColorBar() throws IOException | |
239 | { | |
240 | float[] rgbValues = separation.toRGB(new float[] {tintValue}); | |
241 | colorBar.setBackground(new Color(rgbValues[0], rgbValues[1], rgbValues[2])); | |
244 | 242 | } |
245 | 243 | |
246 | 244 | /** |
247 | 245 | * Set a little border around colorbar. color of the border is the darkest of the colorant. |
248 | 246 | */ |
249 | private void setColorBarBorder() | |
250 | { | |
251 | try | |
252 | { | |
253 | float[] rgbValues = separation.toRGB(new float[] {1}); | |
254 | Color darkest= new Color(rgbValues[0], rgbValues[1], rgbValues[2]); | |
255 | colorBar.setBorder(new BevelBorder(BevelBorder.LOWERED, darkest, darkest)); | |
256 | } | |
257 | catch (IOException e) | |
258 | { | |
259 | throw new RuntimeException(e); | |
260 | } | |
247 | private void setColorBarBorder() throws IOException | |
248 | { | |
249 | float[] rgbValues = separation.toRGB(new float[] {1}); | |
250 | Color darkest= new Color(rgbValues[0], rgbValues[1], rgbValues[2]); | |
251 | colorBar.setBorder(new BevelBorder(BevelBorder.LOWERED, darkest, darkest)); | |
261 | 252 | } |
262 | 253 | |
263 | 254 | private float getFloatRepresentation(int value) |
180 | 180 | cs.beginText(); |
181 | 181 | cs.setFont(font, scale / Math.min(Math.abs(scalingFactorX), Math.abs(scalingFactorY))); |
182 | 182 | // can't use showText() because there's no guarantee we have the unicode |
183 | cs.appendRawCommands(String.format("<%02X> Tj\n", index).getBytes(Charsets.ISO_8859_1)); | |
183 | cs.appendRawCommands(String.format("<%02X> Tj%n", index).getBytes(Charsets.ISO_8859_1)); | |
184 | 184 | cs.endText(); |
185 | 185 | cs.close(); |
186 | 186 | doc.addPage(page); |
49 | 49 | import org.apache.pdfbox.rendering.PDFRenderer; |
50 | 50 | import org.apache.pdfbox.debugger.PDFDebugger; |
51 | 51 | import org.apache.pdfbox.debugger.ui.HighResolutionImageIcon; |
52 | import org.apache.pdfbox.debugger.ui.ImageTypeMenu; | |
53 | import org.apache.pdfbox.debugger.ui.RenderDestinationMenu; | |
52 | 54 | import org.apache.pdfbox.pdmodel.common.PDRectangle; |
53 | 55 | import org.apache.pdfbox.pdmodel.interactive.action.PDAction; |
54 | 56 | import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo; |
77 | 79 | private JLabel label; |
78 | 80 | private ZoomMenu zoomMenu; |
79 | 81 | private RotationMenu rotationMenu; |
82 | private ImageTypeMenu imageTypeMenu; | |
83 | private RenderDestinationMenu renderDestinationMenu; | |
80 | 84 | private final JLabel statuslabel; |
81 | 85 | private final PDPage page; |
82 | 86 | private String labelText = ""; |
228 | 232 | String actionCommand = actionEvent.getActionCommand(); |
229 | 233 | if (ZoomMenu.isZoomMenu(actionCommand) || |
230 | 234 | RotationMenu.isRotationMenu(actionCommand) || |
235 | ImageTypeMenu.isImageTypeMenu(actionCommand) || | |
236 | RenderDestinationMenu.isRenderDestinationMenu(actionCommand) || | |
231 | 237 | actionEvent.getSource() == PDFDebugger.allowSubsampling) |
232 | 238 | { |
233 | 239 | startRendering(); |
239 | 245 | { |
240 | 246 | // render in a background thread: rendering is read-only, so this should be ok, despite |
241 | 247 | // the fact that PDDocument is not officially thread safe |
242 | new RenderWorker(ZoomMenu.getZoomScale(), | |
243 | RotationMenu.getRotationDegrees(), | |
244 | PDFDebugger.allowSubsampling.isSelected() | |
245 | ).execute(); | |
248 | new RenderWorker().execute(); | |
246 | 249 | zoomMenu.setPageZoomScale(ZoomMenu.getZoomScale()); |
247 | 250 | } |
248 | 251 | |
255 | 258 | rotationMenu = RotationMenu.getInstance(); |
256 | 259 | rotationMenu.addMenuListeners(this); |
257 | 260 | rotationMenu.setEnableMenu(true); |
261 | ||
262 | imageTypeMenu = ImageTypeMenu.getInstance(); | |
263 | imageTypeMenu.addMenuListeners(this); | |
264 | imageTypeMenu.setEnableMenu(true); | |
265 | ||
266 | renderDestinationMenu = RenderDestinationMenu.getInstance(); | |
267 | renderDestinationMenu.addMenuListeners(this); | |
268 | renderDestinationMenu.setEnableMenu(true); | |
258 | 269 | |
259 | 270 | PDFDebugger.allowSubsampling.setEnabled(true); |
260 | 271 | PDFDebugger.allowSubsampling.addActionListener(this); |
265 | 276 | { |
266 | 277 | zoomMenu.setEnableMenu(false); |
267 | 278 | rotationMenu.setEnableMenu(false); |
279 | imageTypeMenu.setEnableMenu(false); | |
280 | renderDestinationMenu.setEnableMenu(false); | |
268 | 281 | |
269 | 282 | PDFDebugger.allowSubsampling.setEnabled(false); |
270 | 283 | PDFDebugger.allowSubsampling.removeActionListener(this); |
369 | 382 | */ |
370 | 383 | private final class RenderWorker extends SwingWorker<BufferedImage, Integer> |
371 | 384 | { |
372 | private final float scale; | |
373 | private final int rotation; | |
374 | private final boolean allowSubsampling; | |
375 | ||
376 | private RenderWorker(float scale, int rotation, boolean allowSubsampling) | |
377 | { | |
378 | this.scale = scale; | |
379 | this.rotation = rotation; | |
380 | this.allowSubsampling = allowSubsampling; | |
381 | } | |
382 | ||
383 | 385 | @Override |
384 | 386 | protected BufferedImage doInBackground() throws IOException |
385 | 387 | { |
388 | // rendering can take a long time, so remember all options that are used later | |
389 | float scale = ZoomMenu.getZoomScale(); | |
390 | int rotation = RotationMenu.getRotationDegrees(); | |
391 | ||
386 | 392 | label.setIcon(null); |
387 | 393 | labelText = "Rendering..."; |
388 | 394 | label.setText(labelText); |
395 | ||
389 | 396 | PDFRenderer renderer = new PDFRenderer(document); |
390 | renderer.setSubsamplingAllowed(allowSubsampling); | |
397 | renderer.setSubsamplingAllowed(PDFDebugger.allowSubsampling.isSelected()); | |
398 | ||
391 | 399 | long t0 = System.currentTimeMillis(); |
392 | 400 | statuslabel.setText(labelText); |
393 | BufferedImage bim = renderer.renderImage(pageIndex, scale); | |
401 | BufferedImage bim = renderer.renderImage(pageIndex, scale, ImageTypeMenu.getImageType(), RenderDestinationMenu.getRenderDestination()); | |
394 | 402 | float t = (System.currentTimeMillis() - t0) / 1000f; |
395 | 403 | labelText = "Rendered in " + t + " second" + (t > 1 ? "s" : ""); |
396 | 404 | statuslabel.setText(labelText); |
52 | 52 | import org.apache.pdfbox.cos.COSDictionary; |
53 | 53 | import org.apache.pdfbox.cos.COSFloat; |
54 | 54 | import org.apache.pdfbox.cos.COSName; |
55 | import org.apache.pdfbox.cos.COSNull; | |
55 | 56 | import org.apache.pdfbox.cos.COSNumber; |
56 | 57 | import org.apache.pdfbox.cos.COSStream; |
57 | 58 | import org.apache.pdfbox.cos.COSString; |
456 | 457 | } |
457 | 458 | docu.insertString(docu.getLength(), ">> ", null); |
458 | 459 | } |
460 | else if (obj instanceof COSNull) | |
461 | { | |
462 | docu.insertString(docu.getLength(), "null ", null); | |
463 | } | |
459 | 464 | else |
460 | 465 | { |
461 | 466 | String str = obj.toString(); |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | package org.apache.pdfbox.debugger.ui; | |
18 | ||
19 | import javax.swing.ButtonGroup; | |
20 | import javax.swing.JMenu; | |
21 | import javax.swing.JRadioButtonMenuItem; | |
22 | ||
23 | import org.apache.pdfbox.rendering.ImageType; | |
24 | ||
25 | /** | |
26 | * @author Tilman Hausherr | |
27 | * | |
28 | * A singleton class that provides the imagetype menu for the menubar. To act upon the menu item | |
29 | * selection, the user of the class must add ActionListener which will check for the action command | |
30 | * and act accordingly. | |
31 | */ | |
32 | public final class ImageTypeMenu extends MenuBase | |
33 | { | |
34 | public static final String IMAGETYPE_RGB = "RGB"; | |
35 | public static final String IMAGETYPE_ARGB = "ARGB"; | |
36 | public static final String IMAGETYPE_GRAY = "Gray"; | |
37 | public static final String IMAGETYPE_BITONAL = "Bitonal"; | |
38 | ||
39 | private static ImageTypeMenu instance; | |
40 | private JRadioButtonMenuItem rgbItem; | |
41 | private JRadioButtonMenuItem argbItem; | |
42 | private JRadioButtonMenuItem grayItem; | |
43 | private JRadioButtonMenuItem bitonalItem; | |
44 | ||
45 | /** | |
46 | * Constructor. | |
47 | */ | |
48 | private ImageTypeMenu() | |
49 | { | |
50 | setMenu(createMenu()); | |
51 | } | |
52 | ||
53 | /** | |
54 | * Provides the ImageTypeMenu instance. | |
55 | * @return ImageTypeMenu instance. | |
56 | */ | |
57 | public static ImageTypeMenu getInstance() | |
58 | { | |
59 | if (instance == null) | |
60 | { | |
61 | instance = new ImageTypeMenu(); | |
62 | } | |
63 | return instance; | |
64 | } | |
65 | ||
66 | /** | |
67 | * Set the image type selection. | |
68 | * @param selection String instance. | |
69 | */ | |
70 | public void setImageTypeSelection(String selection) | |
71 | { | |
72 | if (IMAGETYPE_RGB.equals(selection)) | |
73 | { | |
74 | rgbItem.setSelected(true); | |
75 | } | |
76 | else if (IMAGETYPE_ARGB.equals(selection)) | |
77 | { | |
78 | argbItem.setSelected(true); | |
79 | } | |
80 | else if (IMAGETYPE_GRAY.equals(selection)) | |
81 | { | |
82 | grayItem.setSelected(true); | |
83 | } | |
84 | else if (IMAGETYPE_BITONAL.equals(selection)) | |
85 | { | |
86 | bitonalItem.setSelected(true); | |
87 | } | |
88 | else | |
89 | { | |
90 | throw new IllegalArgumentException(); | |
91 | } | |
92 | } | |
93 | ||
94 | public static boolean isImageTypeMenu(String actionCommand) | |
95 | { | |
96 | return IMAGETYPE_RGB.equals(actionCommand) || IMAGETYPE_ARGB.equals(actionCommand) || | |
97 | IMAGETYPE_GRAY.equals(actionCommand) || IMAGETYPE_BITONAL.equals(actionCommand); | |
98 | } | |
99 | ||
100 | public static ImageType getImageType() | |
101 | { | |
102 | if (instance.argbItem.isSelected()) | |
103 | { | |
104 | return ImageType.ARGB; | |
105 | } | |
106 | if (instance.grayItem.isSelected()) | |
107 | { | |
108 | return ImageType.GRAY; | |
109 | } | |
110 | if (instance.bitonalItem.isSelected()) | |
111 | { | |
112 | return ImageType.BINARY; | |
113 | } | |
114 | return ImageType.RGB; | |
115 | } | |
116 | ||
117 | public static ImageType getImageType(String actionCommand) | |
118 | { | |
119 | if (IMAGETYPE_RGB.equals(actionCommand)) | |
120 | { | |
121 | return ImageType.RGB; | |
122 | } | |
123 | else if (IMAGETYPE_ARGB.equals(actionCommand)) | |
124 | { | |
125 | return ImageType.ARGB; | |
126 | } | |
127 | else if (IMAGETYPE_GRAY.equals(actionCommand)) | |
128 | { | |
129 | return ImageType.GRAY; | |
130 | } | |
131 | else if (IMAGETYPE_BITONAL.equals(actionCommand)) | |
132 | { | |
133 | return ImageType.BINARY; | |
134 | } | |
135 | else | |
136 | { | |
137 | throw new IllegalArgumentException(); | |
138 | } | |
139 | } | |
140 | ||
141 | private JMenu createMenu() | |
142 | { | |
143 | JMenu menu = new JMenu(); | |
144 | menu.setText("Image type"); | |
145 | ||
146 | rgbItem = new JRadioButtonMenuItem(); | |
147 | argbItem = new JRadioButtonMenuItem(); | |
148 | grayItem = new JRadioButtonMenuItem(); | |
149 | bitonalItem = new JRadioButtonMenuItem(); | |
150 | rgbItem.setSelected(true); | |
151 | ||
152 | ButtonGroup bg = new ButtonGroup(); | |
153 | bg.add(rgbItem); | |
154 | bg.add(argbItem); | |
155 | bg.add(grayItem); | |
156 | bg.add(bitonalItem); | |
157 | ||
158 | rgbItem.setText(IMAGETYPE_RGB); | |
159 | argbItem.setText(IMAGETYPE_ARGB); | |
160 | grayItem.setText(IMAGETYPE_GRAY); | |
161 | bitonalItem.setText(IMAGETYPE_BITONAL); | |
162 | ||
163 | menu.add(rgbItem); | |
164 | menu.add(argbItem); | |
165 | menu.add(grayItem); | |
166 | menu.add(bitonalItem); | |
167 | ||
168 | return menu; | |
169 | } | |
170 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | package org.apache.pdfbox.debugger.ui; | |
18 | ||
19 | import javax.swing.ButtonGroup; | |
20 | import javax.swing.JMenu; | |
21 | import javax.swing.JRadioButtonMenuItem; | |
22 | ||
23 | import org.apache.pdfbox.rendering.RenderDestination; | |
24 | ||
25 | /** | |
26 | * @author Tilman Hausherr | |
27 | * | |
28 | * A singleton class that provides the RenderDestination menu for the menubar. To act upon the menu | |
29 | * item selection, the user of the class must add ActionListener which will check for the action | |
30 | * command and act accordingly. | |
31 | */ | |
32 | public final class RenderDestinationMenu extends MenuBase | |
33 | { | |
34 | public static final String RENDER_DESTINATION_EXPORT = "Export"; | |
35 | public static final String RENDER_DESTINATION_PRINT = "Print"; | |
36 | public static final String RENDER_DESTINATION_VIEW = "View"; | |
37 | ||
38 | private static RenderDestinationMenu instance; | |
39 | private JRadioButtonMenuItem exportItem; | |
40 | private JRadioButtonMenuItem printItem; | |
41 | private JRadioButtonMenuItem viewItem; | |
42 | ||
43 | /** | |
44 | * Constructor. | |
45 | */ | |
46 | private RenderDestinationMenu() | |
47 | { | |
48 | setMenu(createMenu()); | |
49 | } | |
50 | ||
51 | /** | |
52 | * Provides the RenderDestination instance. | |
53 | * @return RenderDestination instance. | |
54 | */ | |
55 | public static RenderDestinationMenu getInstance() | |
56 | { | |
57 | if (instance == null) | |
58 | { | |
59 | instance = new RenderDestinationMenu(); | |
60 | } | |
61 | return instance; | |
62 | } | |
63 | ||
64 | /** | |
65 | * Set the render destination selection. | |
66 | * @param selection String instance. | |
67 | */ | |
68 | public void setRenderDestinationSelection(String selection) | |
69 | { | |
70 | if (RENDER_DESTINATION_EXPORT.equals(selection)) | |
71 | { | |
72 | exportItem.setSelected(true); | |
73 | } | |
74 | else if (RENDER_DESTINATION_PRINT.equals(selection)) | |
75 | { | |
76 | printItem.setSelected(true); | |
77 | } | |
78 | else if (RENDER_DESTINATION_VIEW.equals(selection)) | |
79 | { | |
80 | viewItem.setSelected(true); | |
81 | } | |
82 | else | |
83 | { | |
84 | throw new IllegalArgumentException(); | |
85 | } | |
86 | } | |
87 | ||
88 | public static boolean isRenderDestinationMenu(String actionCommand) | |
89 | { | |
90 | return RENDER_DESTINATION_EXPORT.equals(actionCommand) || RENDER_DESTINATION_PRINT.equals(actionCommand) || | |
91 | RENDER_DESTINATION_VIEW.equals(actionCommand); | |
92 | } | |
93 | ||
94 | public static RenderDestination getRenderDestination() | |
95 | { | |
96 | if (instance.printItem.isSelected()) | |
97 | { | |
98 | return RenderDestination.PRINT; | |
99 | } | |
100 | if (instance.viewItem.isSelected()) | |
101 | { | |
102 | return RenderDestination.VIEW; | |
103 | } | |
104 | return RenderDestination.EXPORT; | |
105 | } | |
106 | ||
107 | public static RenderDestination getRenderDestination(String actionCommand) | |
108 | { | |
109 | if (RENDER_DESTINATION_EXPORT.equals(actionCommand)) | |
110 | { | |
111 | return RenderDestination.EXPORT; | |
112 | } | |
113 | else if (RENDER_DESTINATION_PRINT.equals(actionCommand)) | |
114 | { | |
115 | return RenderDestination.PRINT; | |
116 | } | |
117 | else if (RENDER_DESTINATION_VIEW.equals(actionCommand)) | |
118 | { | |
119 | return RenderDestination.VIEW; | |
120 | } | |
121 | else | |
122 | { | |
123 | throw new IllegalArgumentException(); | |
124 | } | |
125 | } | |
126 | ||
127 | private JMenu createMenu() | |
128 | { | |
129 | JMenu menu = new JMenu(); | |
130 | menu.setText("Render destination"); | |
131 | ||
132 | exportItem = new JRadioButtonMenuItem(); | |
133 | printItem = new JRadioButtonMenuItem(); | |
134 | viewItem = new JRadioButtonMenuItem(); | |
135 | exportItem.setSelected(true); | |
136 | ||
137 | ButtonGroup bg = new ButtonGroup(); | |
138 | bg.add(exportItem); | |
139 | bg.add(printItem); | |
140 | bg.add(viewItem); | |
141 | ||
142 | exportItem.setText(RENDER_DESTINATION_EXPORT); | |
143 | printItem.setText(RENDER_DESTINATION_PRINT); | |
144 | viewItem.setText(RENDER_DESTINATION_VIEW); | |
145 | ||
146 | menu.add(exportItem); | |
147 | menu.add(printItem); | |
148 | menu.add(viewItem); | |
149 | ||
150 | return menu; | |
151 | } | |
152 | } |
44 | 44 | |
45 | 45 | private float pageZoomScale = 1; |
46 | 46 | private float imageZoomScale = 1; |
47 | private static final int[] ZOOMS = new int[] { 25, 50, 100, 200, 400, 1000 }; | |
47 | private static final int[] ZOOMS = new int[] { 25, 50, 100, 150, 200, 400, 1000, 2000 }; | |
48 | 48 | |
49 | 49 | private static ZoomMenu instance; |
50 | 50 | private final JMenu menu; |
22 | 22 | <parent> |
23 | 23 | <groupId>org.apache.pdfbox</groupId> |
24 | 24 | <artifactId>pdfbox-parent</artifactId> |
25 | <version>2.0.18</version> | |
25 | <version>2.0.19</version> | |
26 | 26 | <relativePath>../parent/pom.xml</relativePath> |
27 | 27 | </parent> |
28 | 28 |
22 | 22 | <parent> |
23 | 23 | <groupId>org.apache.pdfbox</groupId> |
24 | 24 | <artifactId>pdfbox-parent</artifactId> |
25 | <version>2.0.18</version> | |
25 | <version>2.0.19</version> | |
26 | 26 | <relativePath>../parent/pom.xml</relativePath> |
27 | 27 | </parent> |
28 | 28 |
127 | 127 | { |
128 | 128 | for (Entry<String, PDComplexFileSpecification> entry : names.entrySet()) |
129 | 129 | { |
130 | String filename = entry.getKey(); | |
131 | 130 | PDComplexFileSpecification fileSpec = entry.getValue(); |
132 | 131 | PDEmbeddedFile embeddedFile = getEmbeddedFile(fileSpec); |
133 | 132 | if (embeddedFile != null) |
134 | 133 | { |
135 | extractFile(filePath, filename, embeddedFile); | |
134 | extractFile(filePath, fileSpec.getFilename(), embeddedFile); | |
136 | 135 | } |
137 | 136 | } |
138 | 137 | } |
+13
-13
46 | 46 | private static final String XOBJECT_DO = "Do\n"; |
47 | 47 | private static final String SPACE = " "; |
48 | 48 | |
49 | private static final NumberFormat formatDecimal = NumberFormat.getNumberInstance( Locale.US ); | |
49 | private static final NumberFormat FORMATDECIMAL = NumberFormat.getNumberInstance( Locale.US ); | |
50 | 50 | |
51 | 51 | /** |
52 | 52 | * Add a rubber stamp with an jpg image to every page of the given document. |
85 | 85 | PDImageXObject ximage = PDImageXObject.createFromFile(args[2], document); |
86 | 86 | |
87 | 87 | // define and set the target rectangle |
88 | int lowerLeftX = 250; | |
89 | int lowerLeftY = 550; | |
90 | int formWidth = 150; | |
91 | int formHeight = 25; | |
92 | int imgWidth = 50; | |
93 | int imgHeight = 25; | |
88 | float lowerLeftX = 250; | |
89 | float lowerLeftY = 550; | |
90 | float formWidth = 150; | |
91 | float formHeight = 25; | |
92 | float imgWidth = 50; | |
93 | float imgHeight = 25; | |
94 | 94 | |
95 | 95 | PDRectangle rect = new PDRectangle(); |
96 | 96 | rect.setLowerLeftX(lowerLeftX); |
138 | 138 | COSName xObjectId = resources.add(xobject); |
139 | 139 | |
140 | 140 | appendRawCommands( os, SAVE_GRAPHICS_STATE ); |
141 | appendRawCommands( os, formatDecimal.format( width ) ); | |
141 | appendRawCommands( os, FORMATDECIMAL.format( width ) ); | |
142 | 142 | appendRawCommands( os, SPACE ); |
143 | appendRawCommands( os, formatDecimal.format( 0 ) ); | |
143 | appendRawCommands( os, FORMATDECIMAL.format( 0 ) ); | |
144 | 144 | appendRawCommands( os, SPACE ); |
145 | appendRawCommands( os, formatDecimal.format( 0 ) ); | |
145 | appendRawCommands( os, FORMATDECIMAL.format( 0 ) ); | |
146 | 146 | appendRawCommands( os, SPACE ); |
147 | appendRawCommands( os, formatDecimal.format( height ) ); | |
147 | appendRawCommands( os, FORMATDECIMAL.format( height ) ); | |
148 | 148 | appendRawCommands( os, SPACE ); |
149 | appendRawCommands( os, formatDecimal.format( x ) ); | |
149 | appendRawCommands( os, FORMATDECIMAL.format( x ) ); | |
150 | 150 | appendRawCommands( os, SPACE ); |
151 | appendRawCommands( os, formatDecimal.format( y ) ); | |
151 | appendRawCommands( os, FORMATDECIMAL.format( y ) ); | |
152 | 152 | appendRawCommands( os, SPACE ); |
153 | 153 | appendRawCommands( os, CONCATENATE_MATRIX ); |
154 | 154 | appendRawCommands( os, SPACE ); |
36 | 36 | */ |
37 | 37 | public UsingTextMatrix() |
38 | 38 | { |
39 | super(); | |
40 | 39 | } |
41 | 40 | |
42 | 41 | /** |
99 | 98 | // text scaling and translation |
100 | 99 | for (int i=0;i<10;i++) |
101 | 100 | { |
102 | contentStream.setTextMatrix(new Matrix(12 + (i * 6), 0, 0, 12+(i*6), 100, 100+i*50)); | |
101 | contentStream.setTextMatrix(new Matrix(12f + (i * 6), 0, 0, 12f + (i * 6), | |
102 | 100, 100f + i * 50)); | |
103 | 103 | contentStream.showText(message + " " + i); |
104 | 104 | } |
105 | 105 | contentStream.endText(); |
44 | 44 | /** |
45 | 45 | * Entry point. |
46 | 46 | */ |
47 | public static void main(String args[]) throws PrinterException, IOException | |
47 | public static void main(String[] args) throws PrinterException, IOException | |
48 | 48 | { |
49 | 49 | if (args.length != 1) |
50 | 50 | { |
+4
-4
81 | 81 | @Override |
82 | 82 | public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException |
83 | 83 | { |
84 | System.out.printf("appendRectangle %.2f %.2f, %.2f %.2f, %.2f %.2f, %.2f %.2f\n", | |
84 | System.out.printf("appendRectangle %.2f %.2f, %.2f %.2f, %.2f %.2f, %.2f %.2f%n", | |
85 | 85 | p0.getX(), p0.getY(), p1.getX(), p1.getY(), |
86 | 86 | p2.getX(), p2.getY(), p3.getX(), p3.getY()); |
87 | 87 | } |
101 | 101 | @Override |
102 | 102 | public void moveTo(float x, float y) throws IOException |
103 | 103 | { |
104 | System.out.printf("moveTo %.2f %.2f\n", x, y); | |
104 | System.out.printf("moveTo %.2f %.2f%n", x, y); | |
105 | 105 | } |
106 | 106 | |
107 | 107 | @Override |
108 | 108 | public void lineTo(float x, float y) throws IOException |
109 | 109 | { |
110 | System.out.printf("lineTo %.2f %.2f\n", x, y); | |
110 | System.out.printf("lineTo %.2f %.2f%n", x, y); | |
111 | 111 | } |
112 | 112 | |
113 | 113 | @Override |
114 | 114 | public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException |
115 | 115 | { |
116 | System.out.printf("curveTo %.2f %.2f, %.2f %.2f, %.2f %.2f\n", x1, y1, x2, y2, x3, y3); | |
116 | System.out.printf("curveTo %.2f %.2f, %.2f %.2f, %.2f %.2f%n", x1, y1, x2, y2, x3, y3); | |
117 | 117 | } |
118 | 118 | |
119 | 119 | @Override |
+11
-13
67 | 67 | Enumeration<String> aliases = keystore.aliases(); |
68 | 68 | String alias; |
69 | 69 | Certificate cert = null; |
70 | while (aliases.hasMoreElements()) | |
70 | while (cert == null && aliases.hasMoreElements()) | |
71 | 71 | { |
72 | 72 | alias = aliases.nextElement(); |
73 | 73 | setPrivateKey((PrivateKey) keystore.getKey(alias, pin)); |
74 | 74 | Certificate[] certChain = keystore.getCertificateChain(alias); |
75 | if (certChain == null) | |
75 | if (certChain != null) | |
76 | 76 | { |
77 | continue; | |
77 | setCertificateChain(certChain); | |
78 | cert = certChain[0]; | |
79 | if (cert instanceof X509Certificate) | |
80 | { | |
81 | // avoid expired certificate | |
82 | ((X509Certificate) cert).checkValidity(); | |
83 | ||
84 | SigUtils.checkCertificateUsage((X509Certificate) cert); | |
85 | } | |
78 | 86 | } |
79 | setCertificateChain(certChain); | |
80 | cert = certChain[0]; | |
81 | if (cert instanceof X509Certificate) | |
82 | { | |
83 | // avoid expired certificate | |
84 | ((X509Certificate) cert).checkValidity(); | |
85 | ||
86 | SigUtils.checkCertificateUsage((X509Certificate) cert); | |
87 | } | |
88 | break; | |
89 | 87 | } |
90 | 88 | |
91 | 89 | if (cert == null) |
57 | 57 | */ |
58 | 58 | public class DrawPrintTextLocations extends PDFTextStripper |
59 | 59 | { |
60 | private BufferedImage image; | |
61 | 60 | private AffineTransform flipAT; |
62 | 61 | private AffineTransform rotateAT; |
63 | 62 | private AffineTransform transAT; |
202 | 201 | private void stripPage(int page) throws IOException |
203 | 202 | { |
204 | 203 | PDFRenderer pdfRenderer = new PDFRenderer(document); |
205 | image = pdfRenderer.renderImage(page, SCALE); | |
206 | ||
204 | BufferedImage image = pdfRenderer.renderImage(page, SCALE); | |
207 | 205 | PDPage pdPage = document.getPage(page); |
208 | 206 | PDRectangle cropBox = pdPage.getCropBox(); |
209 | 207 |
20 | 20 | <parent> |
21 | 21 | <groupId>org.apache.pdfbox</groupId> |
22 | 22 | <artifactId>pdfbox-parent</artifactId> |
23 | <version>2.0.18</version> | |
23 | <version>2.0.19</version> | |
24 | 24 | <relativePath>../parent/pom.xml</relativePath> |
25 | 25 | </parent> |
26 | 26 |
411 | 411 | { |
412 | 412 | return 0d; |
413 | 413 | } |
414 | return Double.valueOf(sb.toString()); | |
414 | try | |
415 | { | |
416 | return Double.valueOf(sb.toString()); | |
417 | } | |
418 | catch (NumberFormatException ex) | |
419 | { | |
420 | throw new IOException(ex); | |
421 | } | |
415 | 422 | } |
416 | 423 | |
417 | 424 | private CFFFont parseFont(CFFDataInput input, String name, byte[] topDictIndex) throws IOException |
39 | 39 | private static final Log LOG = LogFactory.getLog(Type1CharString.class); |
40 | 40 | |
41 | 41 | private Type1CharStringReader font; |
42 | private final String fontName, glyphName; | |
42 | private final String fontName; | |
43 | private final String glyphName; | |
43 | 44 | private GeneralPath path = null; |
44 | 45 | private int width = 0; |
45 | 46 | private Point2D.Float leftSideBearing = null; |
181 | 182 | } |
182 | 183 | else if ("vmoveto".equals(name)) |
183 | 184 | { |
184 | if (numbers.size() >= 1) | |
185 | if (!numbers.isEmpty()) | |
185 | 186 | { |
186 | 187 | if (isFlex) |
187 | 188 | { |
196 | 197 | } |
197 | 198 | else if ("hmoveto".equals(name)) |
198 | 199 | { |
199 | if (numbers.size() >= 1) | |
200 | if (!numbers.isEmpty()) | |
200 | 201 | { |
201 | 202 | if (isFlex) |
202 | 203 | { |
218 | 219 | } |
219 | 220 | else if ("hlineto".equals(name)) |
220 | 221 | { |
221 | if (numbers.size() >= 1) | |
222 | if (!numbers.isEmpty()) | |
222 | 223 | { |
223 | 224 | rlineTo(numbers.get(0), 0); |
224 | 225 | } |
225 | 226 | } |
226 | 227 | else if ("vlineto".equals(name)) |
227 | 228 | { |
228 | if (numbers.size() >= 1) | |
229 | if (!numbers.isEmpty()) | |
229 | 230 | { |
230 | 231 | rlineTo(0, numbers.get(0)); |
231 | 232 | } |
292 | 293 | } |
293 | 294 | else if ("callothersubr".equals(name)) |
294 | 295 | { |
295 | if (numbers.size() >= 1) | |
296 | if (!numbers.isEmpty()) | |
296 | 297 | { |
297 | 298 | callothersubr(numbers.get(0).intValue()); |
298 | 299 | } |
44 | 44 | static final int CALLOTHERSUBR = 16; |
45 | 45 | static final int POP = 17; |
46 | 46 | |
47 | private final String fontName, glyphName; | |
47 | private final String fontName; | |
48 | private final String glyphName; | |
48 | 49 | |
49 | 50 | /** |
50 | 51 | * Constructs a new Type1CharStringParser object. |
210 | 210 | else if ("hintmask".equals(name) || "cntrmask".equals(name)) |
211 | 211 | { |
212 | 212 | numbers = clearStack(numbers, numbers.size() % 2 != 0); |
213 | if (numbers.size() > 0) | |
213 | if (!numbers.isEmpty()) | |
214 | 214 | { |
215 | 215 | expandStemHints(numbers, false); |
216 | 216 | } |
307 | 307 | |
308 | 308 | private void drawAlternatingLine(List<Number> numbers, boolean horizontal) |
309 | 309 | { |
310 | while (numbers.size() > 0) | |
310 | while (!numbers.isEmpty()) | |
311 | 311 | { |
312 | 312 | addCommand(numbers.subList(0, 1), new CharStringCommand( |
313 | 313 | horizontal ? 6 : 7)); |
30 | 30 | private int vstemCount = 0; |
31 | 31 | private List<Object> sequence = null; |
32 | 32 | @SuppressWarnings("unused") |
33 | private final String fontName, glyphName; | |
33 | private final String fontName; | |
34 | @SuppressWarnings("unused") | |
35 | private final String glyphName; | |
34 | 36 | |
35 | 37 | /** |
36 | 38 | * Constructs a new Type1CharStringParser object for a Type 1-equivalent font. |
390 | 390 | // PDFBOX-3450: ignore <> |
391 | 391 | if (tokenBytes.length > 0) |
392 | 392 | { |
393 | // PDFBOX-4661: avoid overflow of the last byte, all following values are undefined | |
394 | int values = Math.min(end - start, | |
395 | 255 - (tokenBytes[tokenBytes.length - 1] & 0xFF)) + 1; | |
396 | addMappingFrombfrange(result, startCode, values, tokenBytes); | |
393 | // PDFBOX-4720: | |
394 | // some pdfs use the malformed bfrange <0000> <FFFF> <0000>. Add support by adding a identity | |
395 | // mapping for the whole range instead of cutting it after 255 entries | |
396 | // TODO find a more efficient method to represent all values for a identity mapping | |
397 | if (tokenBytes.length == 2 && start == 0 && end == 0xffff | |
398 | && tokenBytes[0] == 0 && tokenBytes[1] == 0) | |
399 | { | |
400 | for (int i = 0; i < 256; i++) | |
401 | { | |
402 | startCode[1] = (byte) i; | |
403 | tokenBytes[1] = (byte) i; | |
404 | addMappingFrombfrange(result, startCode, 0xff, tokenBytes); | |
405 | ||
406 | } | |
407 | } | |
408 | else | |
409 | { | |
410 | // PDFBOX-4661: avoid overflow of the last byte, all following values are undefined | |
411 | int values = Math.min(end - start, | |
412 | 255 - (tokenBytes[tokenBytes.length - 1] & 0xFF)) + 1; | |
413 | addMappingFrombfrange(result, startCode, values, tokenBytes); | |
414 | } | |
397 | 415 | } |
398 | 416 | } |
399 | 417 | } |
35 | 35 | /** |
36 | 36 | * Uses a byte instead of a char buffer for efficiency reasons. |
37 | 37 | */ |
38 | private final byte buffer[]; | |
38 | private final byte[] buffer; | |
39 | 39 | private int bufend = 0; |
40 | 40 | private int bufpos = 0; |
41 | 41 | |
145 | 145 | * {@inheritDoc} |
146 | 146 | */ |
147 | 147 | @Override |
148 | public int read(byte b[], int off, int len) throws IOException | |
148 | public int read(byte[] b, int off, int len) throws IOException | |
149 | 149 | { |
150 | 150 | int leftover = bufend - bufpos; |
151 | 151 | if (len <= leftover) |
36 | 36 | { |
37 | 37 | private static final Log LOG = LogFactory.getLog(CmapSubtable.class); |
38 | 38 | |
39 | private static final long LEAD_OFFSET = 0xD800 - (0x10000 >> 10); | |
40 | private static final long SURROGATE_OFFSET = 0x10000 - (0xD800 << 10) - 0xDC00; | |
39 | private static final long LEAD_OFFSET = 0xD800l - (0x10000 >> 10); | |
40 | private static final long SURROGATE_OFFSET = 0x10000l - (0xD800 << 10) - 0xDC00; | |
41 | 41 | |
42 | 42 | private int platformId; |
43 | 43 | private int platformEncodingId; |
704 | 704 | // encoding record |
705 | 705 | writeUint16(out, CmapTable.PLATFORM_WINDOWS); // platformID |
706 | 706 | writeUint16(out, CmapTable.ENCODING_WIN_UNICODE_BMP); // platformSpecificID |
707 | writeUint32(out, 4 * 2 + 4); // offset | |
707 | writeUint32(out, 12); // offset 4 * 2 + 4 | |
708 | 708 | |
709 | 709 | // build Format 4 subtable (Unicode BMP) |
710 | 710 | Iterator<Entry<Integer, Integer>> it = uniToGID.entrySet().iterator(); |
895 | 895 | if (glyphId <= lastgid) |
896 | 896 | { |
897 | 897 | // copy width and lsb |
898 | offset = glyphId * 4; | |
898 | offset = glyphId * 4l; | |
899 | 899 | lastOffset = copyBytes(is, bos, offset, lastOffset, 4); |
900 | 900 | } |
901 | 901 | else |
905 | 905 | // one time only: copy width from lastgid, whose width applies |
906 | 906 | // to all later glyphs |
907 | 907 | needLastGidWidth = false; |
908 | offset = lastgid * 4; | |
908 | offset = lastgid * 4l; | |
909 | 909 | lastOffset = copyBytes(is, bos, offset, lastOffset, 2); |
910 | 910 | |
911 | 911 | // then go on with lsb from actual glyph (lsb are individual even in monotype fonts) |
912 | 912 | } |
913 | 913 | |
914 | 914 | // copy lsb only, as we are beyond numOfHMetrics |
915 | offset = h.getNumberOfHMetrics() * 4 + (glyphId - h.getNumberOfHMetrics()) * 2; | |
915 | offset = h.getNumberOfHMetrics() * 4l + (glyphId - h.getNumberOfHMetrics()) * 2l; | |
916 | 916 | lastOffset = copyBytes(is, bos, offset, lastOffset, 2); |
917 | 917 | } |
918 | 918 | } |
32 | 32 | |
33 | 33 | <groupId>org.apache.pdfbox</groupId> |
34 | 34 | <artifactId>pdfbox-parent</artifactId> |
35 | <version>2.0.18</version> | |
35 | <version>2.0.19</version> | |
36 | 36 | <packaging>pom</packaging> |
37 | 37 | |
38 | 38 | <name>PDFBox parent</name> |
52 | 52 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
53 | 53 | <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
54 | 54 | |
55 | <bouncycastle.version>1.60</bouncycastle.version> | |
55 | <bouncycastle.version>1.64</bouncycastle.version> | |
56 | 56 | |
57 | 57 | <!-- PDFBOX-4479 to build on jdk6 on newer Jenkins --> |
58 | 58 | <jdk.path>${env.JAVA_HOME}</jdk.path> |
63 | 63 | <dependency> |
64 | 64 | <groupId>junit</groupId> |
65 | 65 | <artifactId>junit</artifactId> |
66 | <version>4.12</version> | |
66 | <version>4.13</version> | |
67 | 67 | <scope>test</scope> |
68 | 68 | </dependency> |
69 | 69 | <dependency> |
193 | 193 | <plugin> |
194 | 194 | <groupId>org.owasp</groupId> |
195 | 195 | <artifactId>dependency-check-maven</artifactId> |
196 | <version>5.2.4</version> | |
196 | <version>5.3.0</version> | |
197 | 197 | <configuration> |
198 | 198 | <failBuildOnAnyVulnerability>true</failBuildOnAnyVulnerability> |
199 | 199 | <!-- https://github.com/jeremylong/DependencyCheck/issues/1574 --> |
526 | 526 | </developers> |
527 | 527 | |
528 | 528 | <scm> |
529 | <connection>scm:svn:http://svn.apache.org/repos/asf/maven/pom/tags/2.0.18/pdfbox-parent</connection> | |
530 | <developerConnection>scm:svn:https://svn.apache.org/repos/asf/maven/pom/tags/2.0.18/pdfbox-parent</developerConnection> | |
531 | <url>http://svn.apache.org/viewvc/maven/pom/tags/2.0.18/pdfbox-parent</url> | |
529 | <connection>scm:svn:http://svn.apache.org/repos/asf/maven/pom/tags/2.0.19/pdfbox-parent</connection> | |
530 | <developerConnection>scm:svn:https://svn.apache.org/repos/asf/maven/pom/tags/2.0.19/pdfbox-parent</developerConnection> | |
531 | <url>http://svn.apache.org/viewvc/maven/pom/tags/2.0.19/pdfbox-parent</url> | |
532 | 532 | </scm> |
533 | 533 | </project> |
22 | 22 | <parent> |
23 | 23 | <groupId>org.apache.pdfbox</groupId> |
24 | 24 | <artifactId>pdfbox-parent</artifactId> |
25 | <version>2.0.18</version> | |
25 | <version>2.0.19</version> | |
26 | 26 | <relativePath>../parent/pom.xml</relativePath> |
27 | 27 | </parent> |
28 | 28 | |
657 | 657 | <sha512>ed55a65bad6f91104846d28e9e7dcb18b11efbf980a74781350551428bbaecba375d9e22af02edf0f91263c7f668e93ec7c52d397898b7640c2d60e7dd3ae940</sha512> |
658 | 658 | </configuration> |
659 | 659 | </execution> |
660 | <execution> | |
661 | <id>PDFBOX-4750</id> | |
662 | <phase>generate-test-resources</phase> | |
663 | <goals> | |
664 | <goal>wget</goal> | |
665 | </goals> | |
666 | <configuration> | |
667 | <url>https://issues.apache.org/jira/secure/attachment/12991833/PDFBOX-4750-test.pdf</url> | |
668 | <outputDirectory>${project.build.directory}/pdfs</outputDirectory> | |
669 | <outputFileName>PDFBOX-4750.pdf</outputFileName> | |
670 | <sha512>add66ca86b5eb3bfd44fd8c273a81695586d76f6e630b714d4b8db6db5aee5b80e7a6d47b9863a42506773c2c0ee2181469d5cd717dba185b70ef5db7ffb80b9</sha512> | |
671 | </configuration> | |
672 | </execution> | |
673 | <execution> | |
674 | <id>PDFBOX-4153</id> | |
675 | <phase>generate-test-resources</phase> | |
676 | <goals> | |
677 | <goal>wget</goal> | |
678 | </goals> | |
679 | <configuration> | |
680 | <url>https://issues.apache.org/jira/secure/attachment/12914331/WXMDXCYRWFDCMOSFQJ5OAJIAFXYRZ5OA.pdf</url> | |
681 | <outputDirectory>${project.build.directory}/pdfs</outputDirectory> | |
682 | <outputFileName>PDFBOX-4153-WXMDXCYRWFDCMOSFQJ5OAJIAFXYRZ5OA.pdf</outputFileName> | |
683 | <sha512>e69a4ee7ba17c384770d8679586273a23fead36a5775669e5ed4d882d738e8d7c5b5cd48deff2ddeea7900efdb13dee8f4899f9db7945d040ddae8628faea465</sha512> | |
684 | </configuration> | |
685 | </execution> | |
686 | <execution> | |
687 | <id>PDFBOX-4490</id> | |
688 | <phase>generate-test-resources</phase> | |
689 | <goals> | |
690 | <goal>wget</goal> | |
691 | </goals> | |
692 | <configuration> | |
693 | <url>https://issues.apache.org/jira/secure/attachment/12962991/NeS1078.pdf</url> | |
694 | <outputDirectory>${project.build.directory}/pdfs</outputDirectory> | |
695 | <outputFileName>PDFBOX-4490.pdf</outputFileName> | |
696 | <sha512>5ae7f232c47c13ed31997eb2c368e7deb1013c1321d70bf79369f8d709b33406191d94c21a5d27b4c4bb48241bafd9328a0a6d2d093d4e540d5044e9503bd099</sha512> | |
697 | </configuration> | |
698 | </execution> | |
660 | 699 | </executions> |
661 | 700 | </plugin> |
662 | 701 | </plugins> |
+6
-3
36 | 36 | @Override |
37 | 37 | public void process(Operator operator, List<COSBase> arguments) throws IOException |
38 | 38 | { |
39 | COSName name = (COSName)arguments.get(0); | |
40 | ||
41 | PDColorSpace cs = context.getResources().getColorSpace(name); | |
39 | COSBase base = arguments.get(0); | |
40 | if (!(base instanceof COSName)) | |
41 | { | |
42 | return; | |
43 | } | |
44 | PDColorSpace cs = context.getResources().getColorSpace((COSName) base); | |
42 | 45 | context.getGraphicsState().setNonStrokingColorSpace(cs); |
43 | 46 | context.getGraphicsState().setNonStrokingColor(cs.getInitialColor()); |
44 | 47 | } |
21 | 21 | import java.util.Arrays; |
22 | 22 | import java.util.Calendar; |
23 | 23 | import java.util.Collection; |
24 | import java.util.LinkedHashMap; | |
25 | 24 | import java.util.List; |
26 | 25 | import java.util.Map; |
27 | 26 | import java.util.Set; |
29 | 28 | import org.apache.pdfbox.io.IOUtils; |
30 | 29 | import org.apache.pdfbox.pdmodel.common.COSObjectable; |
31 | 30 | import org.apache.pdfbox.util.DateConverter; |
31 | import org.apache.pdfbox.util.SmallMap; | |
32 | 32 | |
33 | 33 | /** |
34 | 34 | * This class represents a dictionary where name/value pairs reside. |
44 | 44 | /** |
45 | 45 | * The name-value pairs of this dictionary. The pairs are kept in the order they were added to the dictionary. |
46 | 46 | */ |
47 | protected Map<COSName, COSBase> items = new LinkedHashMap<COSName, COSBase>(); | |
47 | protected Map<COSName, COSBase> items = new SmallMap<COSName, COSBase>(); | |
48 | 48 | |
49 | 49 | /** |
50 | 50 | * Constructor. |
27 | 27 | |
28 | 28 | import org.apache.pdfbox.io.IOUtils; |
29 | 29 | import org.apache.pdfbox.io.ScratchFile; |
30 | import org.apache.pdfbox.multipdf.Splitter; | |
30 | 31 | import org.apache.pdfbox.pdfparser.PDFObjectStreamParser; |
31 | 32 | import org.apache.pdfbox.pdmodel.PDDocument; |
32 | 33 | |
383 | 384 | } |
384 | 385 | |
385 | 386 | /** |
386 | * This will get a list of all available objects. | |
387 | * This will get a list of all available objects. This method works only for loaded PDFs. It | |
388 | * will return an empty list for PDFs created from scratch (this includes PDFs generated within | |
389 | * PDFBox, e.g. by {@link Splitter}). This method will be removed in 3.0. | |
387 | 390 | * |
388 | 391 | * @return A list of all objects, never null. |
389 | 392 | */ |
451 | 454 | /** |
452 | 455 | * This will close all storage and delete the tmp files. |
453 | 456 | * |
454 | * @throws IOException If there is an error close resources. | |
457 | * @throws IOException If there is an error close resources. | |
455 | 458 | */ |
456 | 459 | @Override |
457 | 460 | public void close() throws IOException |
458 | 461 | { |
459 | if (!closed) | |
460 | { | |
461 | // Make sure that: | |
462 | // - first Exception is kept | |
463 | // - all COSStreams are closed | |
464 | // - ScratchFile is closed | |
465 | // - there's a way to see which errors occurred | |
466 | ||
467 | IOException firstException = null; | |
468 | ||
469 | // close all open I/O streams | |
470 | for (COSObject object : getObjects()) | |
462 | if (closed) | |
463 | { | |
464 | return; | |
465 | } | |
466 | ||
467 | // Make sure that: | |
468 | // - first Exception is kept | |
469 | // - all COSStreams are closed | |
470 | // - ScratchFile is closed | |
471 | // - there's a way to see which errors occurred | |
472 | ||
473 | IOException firstException = null; | |
474 | ||
475 | // close all open I/O streams | |
476 | for (COSObject object : getObjects()) | |
477 | { | |
478 | COSBase cosObject = object.getObject(); | |
479 | if (cosObject instanceof COSStream) | |
471 | 480 | { |
472 | COSBase cosObject = object.getObject(); | |
473 | if (cosObject instanceof COSStream) | |
474 | { | |
475 | firstException = IOUtils.closeAndLogException((COSStream) cosObject, LOG, "COSStream", firstException); | |
476 | } | |
481 | firstException = IOUtils.closeAndLogException((COSStream) cosObject, LOG, "COSStream", firstException); | |
477 | 482 | } |
478 | for (COSStream stream : streams) | |
479 | { | |
480 | firstException = IOUtils.closeAndLogException(stream, LOG, "COSStream", firstException); | |
481 | } | |
482 | if (scratchFile != null) | |
483 | { | |
484 | firstException = IOUtils.closeAndLogException(scratchFile, LOG, "ScratchFile", firstException); | |
485 | } | |
486 | closed = true; | |
487 | ||
488 | // rethrow first exception to keep method contract | |
489 | if (firstException != null) | |
490 | { | |
491 | throw firstException; | |
492 | } | |
483 | } | |
484 | for (COSStream stream : streams) | |
485 | { | |
486 | firstException = IOUtils.closeAndLogException(stream, LOG, "COSStream", firstException); | |
487 | } | |
488 | if (scratchFile != null) | |
489 | { | |
490 | firstException = IOUtils.closeAndLogException(scratchFile, LOG, "ScratchFile", firstException); | |
491 | } | |
492 | closed = true; | |
493 | ||
494 | // rethrow first exception to keep method contract | |
495 | if (firstException != null) | |
496 | { | |
497 | throw firstException; | |
493 | 498 | } |
494 | 499 | } |
495 | 500 |
96 | 96 | @Override |
97 | 97 | public int hashCode() |
98 | 98 | { |
99 | return Long.valueOf(number+generation).hashCode(); | |
99 | // most likely generation is 0. Shift number 4 times (fast as multiply) | |
100 | // to support generation numbers up to 15 | |
101 | return Long.valueOf((number << 4) + generation).hashCode(); | |
100 | 102 | } |
101 | 103 | |
102 | 104 | @Override |
22 | 22 | import java.io.InputStream; |
23 | 23 | import java.io.OutputStream; |
24 | 24 | import java.util.ArrayList; |
25 | import java.util.Arrays; | |
26 | import java.util.Iterator; | |
25 | 27 | import java.util.List; |
28 | import java.util.Map.Entry; | |
29 | ||
26 | 30 | import org.apache.commons.logging.Log; |
27 | 31 | import org.apache.commons.logging.LogFactory; |
28 | 32 | import org.apache.pdfbox.filter.DecodeOptions; |
70 | 74 | setInt(COSName.LENGTH, 0); |
71 | 75 | this.scratchFile = scratchFile != null ? scratchFile : ScratchFile.getMainMemoryOnlyInstance(); |
72 | 76 | } |
77 | ||
78 | /** | |
79 | * {@inheritDoc} | |
80 | */ | |
81 | @Override | |
82 | public boolean equals(Object o) { | |
83 | if (o == this) | |
84 | { | |
85 | return true; | |
86 | } | |
87 | ||
88 | if (!(o instanceof COSStream)) | |
89 | { | |
90 | return false; | |
91 | } | |
92 | ||
93 | COSStream toBeCompared = (COSStream) o; | |
94 | ||
95 | if (toBeCompared.size() != size()) | |
96 | { | |
97 | return false; | |
98 | } | |
99 | ||
100 | // compare dictionary content | |
101 | Iterator<Entry<COSName, COSBase>> iter = entrySet().iterator(); | |
102 | while (iter.hasNext()) | |
103 | { | |
104 | Entry<COSName, COSBase> entry = iter.next(); | |
105 | COSName key = entry.getKey(); | |
106 | COSBase value = entry.getValue(); | |
107 | ||
108 | if (!toBeCompared.containsKey(key)) | |
109 | { | |
110 | return false; | |
111 | } | |
112 | else if (value == null) | |
113 | { | |
114 | if (toBeCompared.getItem(key) != null) | |
115 | { | |
116 | return false; | |
117 | } | |
118 | } | |
119 | else if (!value.equals(toBeCompared.getItem(key))) | |
120 | { | |
121 | return false; | |
122 | } | |
123 | } | |
124 | ||
125 | // compare stream content | |
126 | if (!toBeCompared.toTextString().equals(toTextString())) | |
127 | { | |
128 | return false; | |
129 | } | |
130 | ||
131 | return true; | |
132 | } | |
133 | ||
134 | /** | |
135 | * {@inheritDoc} | |
136 | */ | |
137 | @Override | |
138 | public int hashCode() { | |
139 | Object[] members = {items, randomAccess, scratchFile, isWriting}; | |
140 | return Arrays.hashCode(members); | |
141 | } | |
142 | ||
143 | ||
73 | 144 | |
74 | 145 | /** |
75 | 146 | * Throws if the random access backing store has been closed. Helpful for catching cases where |
323 | 323 | } |
324 | 324 | |
325 | 325 | int newPagePosition = (int) (seekToPosition / pageSize); |
326 | if (seekToPosition % pageSize == 0 && seekToPosition == size) | |
327 | { | |
328 | newPagePosition--; // PDFBOX-4756: Prevent seeking a non-yet-existent page... | |
329 | } | |
326 | 330 | |
327 | 331 | currentPage = pageHandler.readPage(pageIndexes[newPagePosition]); |
328 | 332 | currentPagePositionInPageIndexes = newPagePosition; |
1995 | 1995 | int stmGenNumber = readGenerationNumber(); |
1996 | 1996 | readExpectedString(OBJ_MARKER, true); |
1997 | 1997 | int nrOfObjects = 0; |
1998 | byte[] numbersBytes = null; | |
1999 | 1998 | COSStream stream = null; |
2000 | 1999 | COSInputStream is = null; |
2000 | List<Long> objectNumbers = null; | |
2001 | 2001 | try |
2002 | 2002 | { |
2003 | 2003 | COSDictionary dict = parseCOSDictionary(); |
2013 | 2013 | { |
2014 | 2014 | securityHandler.decryptStream(stream, stmObjNumber, stmGenNumber); |
2015 | 2015 | } |
2016 | is = stream.createInputStream(); | |
2017 | numbersBytes = new byte[offsetFirstStream]; | |
2018 | is.read(numbersBytes); | |
2016 | PDFObjectStreamParser strmParser = new PDFObjectStreamParser(stream, document); | |
2017 | objectNumbers = new ArrayList<Long>(nrOfObjects); | |
2018 | for (int i = 0; i < nrOfObjects; i++) | |
2019 | { | |
2020 | objectNumbers.add(strmParser.readObjectNumber()); | |
2021 | strmParser.readLong(); | |
2022 | } | |
2019 | 2023 | } |
2020 | 2024 | catch (IOException exception) |
2021 | 2025 | { |
2034 | 2038 | stream.close(); |
2035 | 2039 | } |
2036 | 2040 | } |
2037 | int start = 0; | |
2038 | // skip spaces | |
2039 | while (start < numbersBytes.length && numbersBytes[start] == 32) | |
2040 | { | |
2041 | start++; | |
2042 | } | |
2043 | String numbersStr = new String(numbersBytes, start, numbersBytes.length - start, | |
2044 | "ISO-8859-1"); | |
2045 | numbersStr = numbersStr.replace('\n', ' ').replace(" ", " "); | |
2046 | String[] numbers = numbersStr.split(" "); | |
2047 | if (numbers.length < nrOfObjects * 2) | |
2041 | if (objectNumbers.size() < nrOfObjects) | |
2048 | 2042 | { |
2049 | 2043 | LOG.debug( |
2050 | 2044 | "Skipped corrupt stream: (" + stmObjNumber + " 0 at offset " + offset); |
2051 | 2045 | continue; |
2052 | 2046 | } |
2053 | 2047 | Map<COSObjectKey, Long> xrefOffset = xrefTrailerResolver.getXrefTable(); |
2054 | for (int i = 0; i < nrOfObjects; i++) | |
2055 | { | |
2056 | try | |
2057 | { | |
2058 | long objNumber = Long.parseLong(numbers[i * 2]); | |
2059 | COSObjectKey objKey = new COSObjectKey(objNumber, 0); | |
2060 | Long existingOffset = bfSearchCOSObjectKeyOffsets.get(objKey); | |
2061 | if (existingOffset != null && existingOffset < 0) | |
2062 | { | |
2063 | // translate stream object key to its offset | |
2064 | COSObjectKey objStmKey = new COSObjectKey(Math.abs(existingOffset), 0); | |
2065 | existingOffset = bfSearchCOSObjectKeyOffsets.get(objStmKey); | |
2066 | } | |
2067 | if (existingOffset == null || offset > existingOffset) | |
2068 | { | |
2069 | bfSearchCOSObjectKeyOffsets.put(objKey, -stmObjNumber); | |
2070 | xrefOffset.put(objKey, -stmObjNumber); | |
2071 | } | |
2072 | } | |
2073 | catch (NumberFormatException exception) | |
2074 | { | |
2075 | LOG.debug("Skipped corrupt object key in stream: " + stmObjNumber); | |
2048 | for (Long objNumber : objectNumbers) | |
2049 | { | |
2050 | COSObjectKey objKey = new COSObjectKey(objNumber, 0); | |
2051 | Long existingOffset = bfSearchCOSObjectKeyOffsets.get(objKey); | |
2052 | if (existingOffset != null && existingOffset < 0) | |
2053 | { | |
2054 | // translate stream object key to its offset | |
2055 | COSObjectKey objStmKey = new COSObjectKey(Math.abs(existingOffset), 0); | |
2056 | existingOffset = bfSearchCOSObjectKeyOffsets.get(objStmKey); | |
2057 | } | |
2058 | if (existingOffset == null || offset > existingOffset) | |
2059 | { | |
2060 | bfSearchCOSObjectKeyOffsets.put(objKey, -stmObjNumber); | |
2061 | xrefOffset.put(objKey, -stmObjNumber); | |
2076 | 2062 | } |
2077 | 2063 | } |
2078 | 2064 | } |
112 | 112 | public void unread(byte[] bytes, int start, int len) throws IOException |
113 | 113 | { |
114 | 114 | input.unread(bytes, start, len); |
115 | position -= len - start; | |
115 | position -= len; | |
116 | 116 | } |
117 | 117 | |
118 | 118 | @Override |
128 | 128 | { |
129 | 129 | off += n; |
130 | 130 | len -= n; |
131 | position += n; | |
132 | 131 | } |
133 | 132 | else |
134 | 133 | { |
81 | 81 | @Override |
82 | 82 | public void unread(byte[] bytes, int start, int len) throws IOException |
83 | 83 | { |
84 | reader.rewind(len - start); | |
84 | reader.rewind(len); | |
85 | 85 | } |
86 | 86 | |
87 | 87 | @Override |
28 | 28 | import org.apache.pdfbox.cos.COSFloat; |
29 | 29 | import org.apache.pdfbox.cos.COSInteger; |
30 | 30 | import org.apache.pdfbox.cos.COSName; |
31 | import org.apache.pdfbox.cos.COSNull; | |
31 | 32 | import org.apache.pdfbox.cos.COSString; |
32 | 33 | import org.apache.pdfbox.util.Charsets; |
33 | 34 | |
143 | 144 | output.write(COSWriter.ARRAY_OPEN); |
144 | 145 | for( int i=0; i<array.size(); i++ ) |
145 | 146 | { |
146 | writeObject( array.get( i ) ); | |
147 | output.write( SPACE ); | |
148 | } | |
149 | ||
150 | output.write( COSWriter.ARRAY_CLOSE ); | |
147 | writeObject(array.get(i)); | |
148 | } | |
149 | output.write(COSWriter.ARRAY_CLOSE); | |
150 | output.write(SPACE); | |
151 | 151 | } |
152 | 152 | else if( o instanceof COSDictionary ) |
153 | 153 | { |
157 | 157 | { |
158 | 158 | if (entry.getValue() != null) |
159 | 159 | { |
160 | writeObject( entry.getKey() ); | |
161 | output.write( SPACE ); | |
162 | writeObject( entry.getValue() ); | |
163 | output.write( SPACE ); | |
160 | writeObject(entry.getKey()); | |
161 | writeObject(entry.getValue()); | |
164 | 162 | } |
165 | 163 | } |
166 | 164 | output.write( COSWriter.DICT_CLOSE ); |
172 | 170 | if (op.getName().equals(OperatorName.BEGIN_INLINE_IMAGE)) |
173 | 171 | { |
174 | 172 | output.write(OperatorName.BEGIN_INLINE_IMAGE.getBytes(Charsets.ISO_8859_1)); |
173 | output.write(EOL); | |
175 | 174 | COSDictionary dic = op.getImageParameters(); |
176 | 175 | for( COSName key : dic.keySet() ) |
177 | 176 | { |
194 | 193 | output.write( EOL ); |
195 | 194 | } |
196 | 195 | } |
196 | else if (o instanceof COSNull) | |
197 | { | |
198 | output.write("null".getBytes(Charsets.ISO_8859_1)); | |
199 | output.write(SPACE); | |
200 | } | |
197 | 201 | else |
198 | 202 | { |
199 | 203 | throw new IOException( "Error:Unknown type in content stream:" + o ); |
1427 | 1427 | * Writes a real number to the content stream. |
1428 | 1428 | * @param real |
1429 | 1429 | * @throws java.io.IOException |
1430 | * @throws IllegalArgumentException if the parameter is not a finite number | |
1430 | 1431 | */ |
1431 | 1432 | protected void writeOperand(float real) throws IOException |
1432 | 1433 | { |
1434 | if (Float.isInfinite(real) || Float.isNaN(real)) | |
1435 | { | |
1436 | throw new IllegalArgumentException(real + " is not a finite number"); | |
1437 | } | |
1438 | ||
1433 | 1439 | int byteCount = NumberFormatUtil.formatFloatFast(real, formatDecimal.getMaximumFractionDigits(), formatBuffer); |
1434 | 1440 | |
1435 | 1441 | if (byteCount == -1) |
2465 | 2465 | * @param real the float value to be added to the content stream. |
2466 | 2466 | * |
2467 | 2467 | * @throws IOException if something went wrong |
2468 | * @throws IllegalArgumentException if the parameter is not a finite number | |
2468 | 2469 | */ |
2469 | 2470 | protected void writeOperand(float real) throws IOException |
2470 | 2471 | { |
2472 | if (Float.isInfinite(real) || Float.isNaN(real)) | |
2473 | { | |
2474 | throw new IllegalArgumentException(real + " is not a finite number"); | |
2475 | } | |
2476 | ||
2471 | 2477 | int byteCount = NumberFormatUtil.formatFloatFast(real, formatDecimal.getMaximumFractionDigits(), formatBuffer); |
2472 | 2478 | |
2473 | 2479 | if (byteCount == -1) |
380 | 380 | private void visitPage(COSDictionary current) |
381 | 381 | { |
382 | 382 | index++; |
383 | found = searched.equals(current); | |
383 | found = searched == current; | |
384 | 384 | } |
385 | 385 | } |
386 | 386 |
42 | 42 | private final COSArray array; |
43 | 43 | private final List<E> actual; |
44 | 44 | |
45 | // indicates that the list has been filtered | |
46 | // i.e. the number of entries in array and actual differ | |
47 | private boolean isFiltered = false; | |
48 | ||
45 | 49 | private COSDictionary parentDict; |
46 | 50 | private COSName dictKey; |
47 | 51 | |
55 | 59 | } |
56 | 60 | |
57 | 61 | /** |
58 | * Constructor. | |
62 | * Create the COSArrayList specifing the List and the backing COSArray. | |
63 | * | |
64 | * <p>User of this constructor need to ensure that the entries in the List and | |
65 | * the backing COSArray are matching i.e. the COSObject of the List entry is | |
66 | * included in the COSArray. | |
67 | * | |
68 | * <p>If the number of entries in the List and the COSArray differ | |
69 | * it is assumed that the List has been filtered. In that case the COSArrayList | |
70 | * shall only be used for reading purposes and no longer for updating. | |
59 | 71 | * |
60 | 72 | * @param actualList The list of standard java objects |
61 | 73 | * @param cosArray The COS array object to sync to. |
64 | 76 | { |
65 | 77 | actual = actualList; |
66 | 78 | array = cosArray; |
79 | ||
80 | // if the number of entries differs this may come from a filter being | |
81 | // applied at the PDModel level | |
82 | if (actual.size() != array.size()) { | |
83 | isFiltered = true; | |
84 | } | |
67 | 85 | } |
68 | 86 | |
69 | 87 | /** |
199 | 217 | @Override |
200 | 218 | public boolean remove(Object o) |
201 | 219 | { |
220 | ||
221 | if (isFiltered) { | |
222 | throw new UnsupportedOperationException("removing entries from a filtered List is not permitted"); | |
223 | } | |
224 | ||
202 | 225 | boolean retval = true; |
203 | 226 | int index = actual.indexOf( o ); |
204 | 227 | if( index >= 0 ) |
205 | 228 | { |
206 | retval = actual.remove( o ); | |
207 | ||
208 | if (o instanceof COSObjectable) { | |
209 | COSBase cosBase = ((COSObjectable) o).getCOSObject(); | |
210 | retval = array.remove(cosBase); | |
211 | } | |
229 | actual.remove( index ); | |
230 | array.remove( index ); | |
212 | 231 | } |
213 | 232 | else |
214 | 233 | { |
232 | 251 | @Override |
233 | 252 | public boolean addAll(Collection<? extends E> c) |
234 | 253 | { |
254 | if (isFiltered) { | |
255 | throw new UnsupportedOperationException("Apping to a filtered List is not permitted"); | |
256 | } | |
257 | ||
235 | 258 | //when adding if there is a parentDict then change the item |
236 | 259 | //in the dictionary from a single item to an array. |
237 | 260 | if( parentDict != null && c.size() > 0) |
251 | 274 | @Override |
252 | 275 | public boolean addAll(int index, Collection<? extends E> c) |
253 | 276 | { |
277 | ||
278 | if (isFiltered) { | |
279 | throw new UnsupportedOperationException("Inserting to a filtered List is not permitted"); | |
280 | } | |
281 | ||
254 | 282 | //when adding if there is a parentDict then change the item |
255 | 283 | //in the dictionary from a single item to an array. |
256 | 284 | if( parentDict != null && c.size() > 0) |
490 | 518 | @Override |
491 | 519 | public boolean removeAll(Collection<?> c) |
492 | 520 | { |
493 | array.removeAll( toCOSObjectList( c ) ); | |
521 | for (Iterator iterator = c.iterator(); iterator.hasNext();) | |
522 | { | |
523 | COSBase itemCOSBase = ((COSObjectable)iterator.next()).getCOSObject(); | |
524 | // remove all indirect objects too by dereferencing them | |
525 | // before doing the comparison | |
526 | for (int i=array.size()-1; i>=0; i--) | |
527 | { | |
528 | if (itemCOSBase.equals(array.getObject(i))) | |
529 | { | |
530 | array.remove(i); | |
531 | } | |
532 | } | |
533 | } | |
494 | 534 | return actual.removeAll( c ); |
495 | 535 | } |
496 | 536 | |
500 | 540 | @Override |
501 | 541 | public boolean retainAll(Collection<?> c) |
502 | 542 | { |
503 | array.retainAll( toCOSObjectList( c ) ); | |
543 | for (Iterator iterator = c.iterator(); iterator.hasNext();) | |
544 | { | |
545 | COSBase itemCOSBase = ((COSObjectable)iterator.next()).getCOSObject(); | |
546 | // remove all indirect objects too by dereferencing them | |
547 | // before doing the comparison | |
548 | for (int i=array.size()-1; i>=0; i--) | |
549 | { | |
550 | if (!itemCOSBase.equals(array.getObject(i))) | |
551 | { | |
552 | array.remove(i); | |
553 | } | |
554 | } | |
555 | } | |
504 | 556 | return actual.retainAll( c ); |
505 | 557 | } |
506 | 558 | |
554 | 606 | @Override |
555 | 607 | public E set(int index, E element) |
556 | 608 | { |
609 | ||
610 | if (isFiltered) { | |
611 | throw new UnsupportedOperationException("Replacing an element in a filtered List is not permitted"); | |
612 | } | |
613 | ||
557 | 614 | if( element instanceof String ) |
558 | 615 | { |
559 | 616 | COSString item = new COSString( (String)element ); |
580 | 637 | @Override |
581 | 638 | public void add(int index, E element) |
582 | 639 | { |
640 | if (isFiltered) { | |
641 | throw new UnsupportedOperationException("Adding an element in a filtered List is not permitted"); | |
642 | } | |
643 | ||
583 | 644 | //when adding if there is a parentDict then change the item |
584 | 645 | //in the dictionary from a single item to an array. |
585 | 646 | if( parentDict != null ) |
606 | 667 | @Override |
607 | 668 | public E remove(int index) |
608 | 669 | { |
609 | E toBeRemoved = actual.get(index); | |
610 | remove(toBeRemoved); | |
611 | return toBeRemoved; | |
670 | if (isFiltered) { | |
671 | throw new UnsupportedOperationException("removing entries from a filtered List is not permitted"); | |
672 | } | |
673 | ||
674 | array.remove(index); | |
675 | return actual.remove(index); | |
612 | 676 | } |
613 | 677 | |
614 | 678 | /** |
126 | 126 | @Override |
127 | 127 | public void putAll(Map<? extends K, ? extends V> t) |
128 | 128 | { |
129 | throw new RuntimeException( "Not yet implemented" ); | |
129 | throw new UnsupportedOperationException("Not yet implemented"); | |
130 | 130 | } |
131 | 131 | |
132 | 132 | /** |
134 | 134 | */ |
135 | 135 | public void setKids( List<? extends PDNameTreeNode<T>> kids ) |
136 | 136 | { |
137 | if (kids != null && kids.size() > 0) | |
137 | if (kids != null && !kids.isEmpty()) | |
138 | 138 | { |
139 | 139 | for (PDNameTreeNode<T> kidsNode : kids) |
140 | 140 | { |
166 | 166 | else |
167 | 167 | { |
168 | 168 | List<PDNameTreeNode<T>> kids = getKids(); |
169 | if (kids != null && kids.size() > 0) | |
169 | if (kids != null && !kids.isEmpty()) | |
170 | 170 | { |
171 | 171 | PDNameTreeNode<T> firstKid = kids.get(0); |
172 | 172 | PDNameTreeNode<T> lastKid = kids.get(kids.size() - 1); |
+7
-20
34 | 34 | { |
35 | 35 | |
36 | 36 | /** |
37 | * Constructor. | |
38 | * | |
39 | * @param document {@inheritDoc} | |
37 | * {@inheritDoc} | |
40 | 38 | */ |
41 | 39 | public PDEmbeddedFile( PDDocument document ) |
42 | 40 | { |
43 | 41 | super( document ); |
44 | 42 | getCOSObject().setName(COSName.TYPE, "EmbeddedFile" ); |
45 | ||
46 | } | |
47 | ||
48 | /** | |
49 | * Constructor. | |
50 | * | |
51 | * @param str The stream parameter. | |
43 | } | |
44 | ||
45 | /** | |
46 | * {@inheritDoc} | |
52 | 47 | */ |
53 | 48 | public PDEmbeddedFile( COSStream str ) |
54 | 49 | { |
56 | 51 | } |
57 | 52 | |
58 | 53 | /** |
59 | * Constructor. | |
60 | * | |
61 | * @param doc {@inheritDoc} | |
62 | * @param str {@inheritDoc} | |
63 | * | |
64 | * @throws IOException {@inheritDoc} | |
54 | * {@inheritDoc} | |
65 | 55 | */ |
66 | 56 | public PDEmbeddedFile( PDDocument doc, InputStream str ) throws IOException |
67 | 57 | { |
70 | 60 | } |
71 | 61 | |
72 | 62 | /** |
73 | * Constructor. | |
74 | * | |
75 | * @param doc {@inheritDoc} | |
76 | * @param input {@inheritDoc} | |
63 | * {@inheritDoc} | |
77 | 64 | * @param filter Filter to apply to the stream. |
78 | 65 | * |
79 | 66 | * @throws IOException {@inheritDoc} |
624 | 624 | gid = cmapMacRoman.getGlyphId(code); |
625 | 625 | } |
626 | 626 | |
627 | // PDFBOX-4755 / PDF.js #5501 | |
628 | if (gid == 0 && cmapWinUnicode != null) | |
629 | { | |
630 | gid = cmapWinUnicode.getGlyphId(code); | |
631 | } | |
632 | ||
627 | 633 | // PDFBOX-3965: fallback for font has that the symbol flag but isn't |
628 | 634 | if (gid == 0 && cmapWinUnicode != null && encoding != null) |
629 | 635 | { |
677 | 683 | { |
678 | 684 | cmapMacRoman = cmap; |
679 | 685 | } |
686 | else if (CmapTable.PLATFORM_UNICODE == cmap.getPlatformId() | |
687 | && CmapTable.ENCODING_UNICODE_1_0 == cmap.getPlatformEncodingId()) | |
688 | { | |
689 | // PDFBOX-4755 / PDF.js #5501 | |
690 | cmapWinUnicode = cmap; | |
691 | } | |
680 | 692 | } |
681 | 693 | } |
682 | 694 | cmapInitialized = true; |
20 | 20 | import java.io.InputStream; |
21 | 21 | import org.apache.pdfbox.contentstream.PDContentStream; |
22 | 22 | import org.apache.pdfbox.cos.COSArray; |
23 | import org.apache.pdfbox.cos.COSBase; | |
23 | 24 | import org.apache.pdfbox.cos.COSDictionary; |
24 | 25 | import org.apache.pdfbox.cos.COSFloat; |
25 | 26 | import org.apache.pdfbox.cos.COSName; |
29 | 30 | import org.apache.pdfbox.pdmodel.ResourceCache; |
30 | 31 | import org.apache.pdfbox.pdmodel.common.PDRectangle; |
31 | 32 | import org.apache.pdfbox.pdmodel.common.PDStream; |
33 | import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDPropertyList; | |
32 | 34 | import org.apache.pdfbox.pdmodel.graphics.PDXObject; |
33 | 35 | import org.apache.pdfbox.util.Matrix; |
34 | 36 | |
257 | 259 | { |
258 | 260 | getCOSObject().setInt(COSName.STRUCT_PARENTS, structParent); |
259 | 261 | } |
262 | ||
263 | /** | |
264 | * This will get the optional content group or optional content membership dictionary. | |
265 | * | |
266 | * @return The optional content group or optional content membership dictionary or null if there | |
267 | * is none. | |
268 | */ | |
269 | public PDPropertyList getOptionalContent() | |
270 | { | |
271 | COSBase base = getCOSObject().getDictionaryObject(COSName.OC); | |
272 | if (base instanceof COSDictionary) | |
273 | { | |
274 | return PDPropertyList.create((COSDictionary) base); | |
275 | } | |
276 | return null; | |
277 | } | |
278 | ||
279 | /** | |
280 | * Sets the optional content group or optional content membership dictionary. | |
281 | * | |
282 | * @param oc The optional content group or optional content membership dictionary. | |
283 | */ | |
284 | public void setOptionalContent(PDPropertyList oc) | |
285 | { | |
286 | getCOSObject().setItem(COSName.OC, oc); | |
287 | } | |
260 | 288 | } |
35 | 35 | import org.apache.commons.logging.LogFactory; |
36 | 36 | import org.apache.pdfbox.cos.COSArray; |
37 | 37 | import org.apache.pdfbox.cos.COSBase; |
38 | import org.apache.pdfbox.cos.COSDictionary; | |
38 | 39 | import org.apache.pdfbox.cos.COSInputStream; |
39 | 40 | import org.apache.pdfbox.cos.COSName; |
40 | 41 | import org.apache.pdfbox.cos.COSObject; |
46 | 47 | import org.apache.pdfbox.pdmodel.PDResources; |
47 | 48 | import org.apache.pdfbox.pdmodel.common.PDMetadata; |
48 | 49 | import org.apache.pdfbox.pdmodel.common.PDStream; |
50 | import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDPropertyList; | |
49 | 51 | import org.apache.pdfbox.pdmodel.graphics.PDXObject; |
50 | 52 | import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; |
51 | 53 | import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; |
899 | 901 | return null; |
900 | 902 | } |
901 | 903 | } |
904 | ||
905 | /** | |
906 | * This will get the optional content group or optional content membership dictionary. | |
907 | * | |
908 | * @return The optional content group or optional content membership dictionary or null if there | |
909 | * is none. | |
910 | */ | |
911 | public PDPropertyList getOptionalContent() | |
912 | { | |
913 | COSBase base = getCOSObject().getDictionaryObject(COSName.OC); | |
914 | if (base instanceof COSDictionary) | |
915 | { | |
916 | return PDPropertyList.create((COSDictionary) base); | |
917 | } | |
918 | return null; | |
919 | } | |
920 | ||
921 | /** | |
922 | * Sets the optional content group or optional content membership dictionary. | |
923 | * | |
924 | * @param oc The optional content group or optional content membership dictionary. | |
925 | */ | |
926 | public void setOptionalContent(PDPropertyList oc) | |
927 | { | |
928 | getCOSObject().setItem(COSName.OC, oc); | |
929 | } | |
902 | 930 | } |
325 | 325 | in = new ByteArrayInputStream(out.toByteArray()); |
326 | 326 | } |
327 | 327 | } |
328 | return new ByteArrayInputStream(out.toByteArray()); | |
328 | return in; | |
329 | 329 | } |
330 | 330 | |
331 | 331 | @Override |
390 | 390 | @Override |
391 | 391 | public String getSuffix() |
392 | 392 | { |
393 | // TODO implement me | |
394 | return null; | |
393 | List<String> filters = getFilters(); | |
394 | ||
395 | if (filters == null || filters.isEmpty()) | |
396 | { | |
397 | return "png"; | |
398 | } | |
399 | if (filters.contains(COSName.DCT_DECODE.getName()) || | |
400 | filters.contains(COSName.DCT_DECODE_ABBREVIATION.getName())) | |
401 | { | |
402 | return "jpg"; | |
403 | } | |
404 | if (filters.contains(COSName.CCITTFAX_DECODE.getName()) || | |
405 | filters.contains(COSName.CCITTFAX_DECODE_ABBREVIATION.getName())) | |
406 | { | |
407 | return "tiff"; | |
408 | } | |
409 | // JPX and JBIG2 don't exist for inline images | |
410 | return "png"; | |
395 | 411 | } |
396 | 412 | } |
+29
-0
195 | 195 | } |
196 | 196 | |
197 | 197 | /** |
198 | * {@inheritDoc} | |
199 | */ | |
200 | @Override | |
201 | public boolean equals (Object o) { | |
202 | if (o == this) | |
203 | { | |
204 | return true; | |
205 | } | |
206 | ||
207 | if (!(o instanceof PDAnnotation)) | |
208 | { | |
209 | return false; | |
210 | } | |
211 | ||
212 | COSDictionary toBeCompared = ((PDAnnotation) o).getCOSObject(); | |
213 | return toBeCompared.equals(getCOSObject()); | |
214 | } | |
215 | ||
216 | /** | |
217 | * {@inheritDoc} | |
218 | */ | |
219 | @Override | |
220 | public int hashCode() | |
221 | { | |
222 | return dictionary.hashCode(); | |
223 | } | |
224 | ||
225 | ||
226 | /** | |
198 | 227 | * The annotation rectangle, defining the location of the annotation on the page in default user space units. This |
199 | 228 | * is usually required and should not return null on valid PDF documents. But where this is a parent form field with |
200 | 229 | * children, such as radio button collections then the rectangle will be null. |
+6
-6
868 | 868 | PDAppearanceHandler appearanceHandler = null; |
869 | 869 | if (SUB_TYPE_CARET.equals(getSubtype())) |
870 | 870 | { |
871 | appearanceHandler = new PDCaretAppearanceHandler(this); | |
871 | appearanceHandler = new PDCaretAppearanceHandler(this, document); | |
872 | 872 | } |
873 | 873 | else if (SUB_TYPE_FREETEXT.equals(getSubtype())) |
874 | 874 | { |
875 | appearanceHandler = new PDFreeTextAppearanceHandler(this); | |
875 | appearanceHandler = new PDFreeTextAppearanceHandler(this, document); | |
876 | 876 | } |
877 | 877 | else if (SUB_TYPE_INK.equals(getSubtype())) |
878 | 878 | { |
879 | appearanceHandler = new PDInkAppearanceHandler(this); | |
879 | appearanceHandler = new PDInkAppearanceHandler(this, document); | |
880 | 880 | } |
881 | 881 | else if (SUB_TYPE_POLYGON.equals(getSubtype())) |
882 | 882 | { |
883 | appearanceHandler = new PDPolygonAppearanceHandler(this); | |
883 | appearanceHandler = new PDPolygonAppearanceHandler(this, document); | |
884 | 884 | } |
885 | 885 | else if (SUB_TYPE_POLYLINE.equals(getSubtype())) |
886 | 886 | { |
887 | appearanceHandler = new PDPolylineAppearanceHandler(this); | |
887 | appearanceHandler = new PDPolylineAppearanceHandler(this, document); | |
888 | 888 | } |
889 | 889 | else if (SUB_TYPE_SOUND.equals(getSubtype())) |
890 | 890 | { |
891 | appearanceHandler = new PDSoundAppearanceHandler(this); | |
891 | appearanceHandler = new PDSoundAppearanceHandler(this, document); | |
892 | 892 | } |
893 | 893 | |
894 | 894 | if (appearanceHandler != null) |
+10
-9
130 | 130 | return getCOSObject().getNameAsString(COSName.SUBTYPE); |
131 | 131 | } |
132 | 132 | |
133 | /** | |
133 | /** | |
134 | 134 | * Set a custom appearance handler for generating the annotations appearance streams. |
135 | * | |
135 | * | |
136 | 136 | * @param appearanceHandler |
137 | 137 | */ |
138 | @Override | |
138 | 139 | public void setCustomAppearanceHandler(PDAppearanceHandler appearanceHandler) |
139 | 140 | { |
140 | 141 | customAppearanceHandler = appearanceHandler; |
154 | 155 | PDAppearanceHandler appearanceHandler = null; |
155 | 156 | if (SUB_TYPE_HIGHLIGHT.equals(getSubtype())) |
156 | 157 | { |
157 | appearanceHandler = new PDHighlightAppearanceHandler(this); | |
158 | appearanceHandler = new PDHighlightAppearanceHandler(this, document); | |
158 | 159 | } |
159 | else if (SUB_TYPE_SQUIGGLY.equals(getSubtype())) | |
160 | else if (SUB_TYPE_SQUIGGLY.equals(getSubtype())) | |
160 | 161 | { |
161 | appearanceHandler = new PDSquigglyAppearanceHandler(this); | |
162 | appearanceHandler = new PDSquigglyAppearanceHandler(this, document); | |
162 | 163 | } |
163 | else if (SUB_TYPE_STRIKEOUT.equals(getSubtype())) | |
164 | else if (SUB_TYPE_STRIKEOUT.equals(getSubtype())) | |
164 | 165 | { |
165 | appearanceHandler = new PDStrikeoutAppearanceHandler(this); | |
166 | appearanceHandler = new PDStrikeoutAppearanceHandler(this, document); | |
166 | 167 | } |
167 | else if (SUB_TYPE_UNDERLINE.equals(getSubtype())) | |
168 | else if (SUB_TYPE_UNDERLINE.equals(getSubtype())) | |
168 | 169 | { |
169 | appearanceHandler = new PDUnderlineAppearanceHandler(this); | |
170 | appearanceHandler = new PDUnderlineAppearanceHandler(this, document); | |
170 | 171 | } |
171 | 172 | |
172 | 173 | if (appearanceHandler != null) |
+30
-25
290 | 290 | cs.addRect(xOffset, clipY, clipWidth, clipHeight); |
291 | 291 | cs.clip(); |
292 | 292 | |
293 | cs.beginText(); | |
294 | cs.setFont(font, fontSize); | |
295 | cs.setNonStrokingColor(textColor.getComponents()); | |
296 | AppearanceStyle appearanceStyle = new AppearanceStyle(); | |
297 | appearanceStyle.setFont(font); | |
298 | appearanceStyle.setFontSize(fontSize); | |
299 | PlainTextFormatter formatter = new PlainTextFormatter.Builder(cs) | |
300 | .style(appearanceStyle) | |
301 | .text(new PlainText(annotation.getContents())) | |
302 | .width(width - ab.width * 4) | |
303 | .wrapLines(true) | |
304 | .initialOffset(xOffset, yOffset) | |
305 | // Adobe ignores the /Q | |
306 | //.textAlign(annotation.getQ()) | |
307 | .build(); | |
308 | try | |
309 | { | |
310 | formatter.format(); | |
311 | } | |
312 | catch (IllegalArgumentException ex) | |
313 | { | |
314 | throw new IOException(ex); | |
315 | } | |
316 | cs.endText(); | |
317 | ||
293 | if (annotation.getContents() != null) | |
294 | { | |
295 | cs.beginText(); | |
296 | cs.setFont(font, fontSize); | |
297 | cs.setNonStrokingColor(textColor.getComponents()); | |
298 | AppearanceStyle appearanceStyle = new AppearanceStyle(); | |
299 | appearanceStyle.setFont(font); | |
300 | appearanceStyle.setFontSize(fontSize); | |
301 | PlainTextFormatter formatter = new PlainTextFormatter.Builder(cs) | |
302 | .style(appearanceStyle) | |
303 | .text(new PlainText(annotation.getContents())) | |
304 | .width(width - ab.width * 4) | |
305 | .wrapLines(true) | |
306 | .initialOffset(xOffset, yOffset) | |
307 | // Adobe ignores the /Q | |
308 | //.textAlign(annotation.getQ()) | |
309 | .build(); | |
310 | try | |
311 | { | |
312 | formatter.format(); | |
313 | } | |
314 | catch (IllegalArgumentException ex) | |
315 | { | |
316 | throw new IOException(ex); | |
317 | } | |
318 | finally | |
319 | { | |
320 | cs.endText(); | |
321 | } | |
322 | } | |
318 | 323 | |
319 | 324 | if (pathsArray.length > 0) |
320 | 325 | { |
+0
-3
20 | 20 | import org.apache.commons.logging.LogFactory; |
21 | 21 | import org.apache.pdfbox.cos.COSDictionary; |
22 | 22 | import org.apache.pdfbox.cos.COSName; |
23 | import org.apache.pdfbox.cos.COSStream; | |
24 | 23 | import org.apache.pdfbox.io.IOUtils; |
25 | 24 | import org.apache.pdfbox.pdmodel.PDFormContentStream; |
26 | 25 | import org.apache.pdfbox.pdmodel.PDResources; |
129 | 128 | r1.setBlendMode(BlendMode.MULTIPLY); |
130 | 129 | cs.setGraphicsStateParameters(r0); |
131 | 130 | cs.setGraphicsStateParameters(r1); |
132 | //TODO replace with document.getDocument().createCOSStream() | |
133 | // or call new PDFormXObject(document) | |
134 | 131 | PDFormXObject frm1 = new PDFormXObject(createCOSStream()); |
135 | 132 | PDFormXObject frm2 = new PDFormXObject(createCOSStream()); |
136 | 133 | frm1.setResources(new PDResources()); |
+35
-1
78 | 78 | /** |
79 | 79 | * The default font size used for multiline text |
80 | 80 | */ |
81 | private static final float DEFAULT_FONT_SIZE = 12; | |
81 | private static final float DEFAULT_FONT_SIZE = 12; | |
82 | ||
83 | /** | |
84 | * The minimum/maximum font sizes used for multiline text auto sizing | |
85 | */ | |
86 | private static final float MINIMUM_FONT_SIZE = 4; | |
87 | private static final float MAXIMUM_FONT_SIZE = 300; | |
82 | 88 | |
83 | 89 | /** |
84 | 90 | * The default padding applied by Acrobat to the fields bbox. |
786 | 792 | { |
787 | 793 | if (isMultiLine()) |
788 | 794 | { |
795 | PlainText textContent = new PlainText(value); | |
796 | if (textContent.getParagraphs() != null) | |
797 | { | |
798 | float width = contentRect.getWidth() - contentRect.getLowerLeftX(); | |
799 | float fs = MINIMUM_FONT_SIZE; | |
800 | while (fs <= MAXIMUM_FONT_SIZE) | |
801 | { | |
802 | // determine the number of lines needed for this font and contentRect | |
803 | int numLines = 0; | |
804 | for (PlainText.Paragraph paragraph : textContent.getParagraphs()) | |
805 | { | |
806 | numLines += paragraph.getLines(font, fs, width).size(); | |
807 | } | |
808 | // calculate the height required for this font size | |
809 | float fontScaleY = fs / FONTSCALE; | |
810 | float leading = font.getBoundingBox().getHeight() * fontScaleY; | |
811 | float height = leading * numLines; | |
812 | ||
813 | // if this font size didn't fit, use the prior size that did fit | |
814 | if (height > contentRect.getHeight()) | |
815 | { | |
816 | return Math.max(fs - 1, MINIMUM_FONT_SIZE); | |
817 | } | |
818 | fs++; | |
819 | } | |
820 | return Math.min(fs, MAXIMUM_FONT_SIZE); | |
821 | } | |
822 | ||
789 | 823 | // Acrobat defaults to 12 for multiline text with size 0 |
790 | 824 | return DEFAULT_FONT_SIZE; |
791 | 825 | } |
83 | 83 | import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; |
84 | 84 | import org.apache.pdfbox.pdmodel.graphics.form.PDTransparencyGroup; |
85 | 85 | import org.apache.pdfbox.pdmodel.graphics.image.PDImage; |
86 | import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; | |
86 | 87 | import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup; |
87 | 88 | import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup.RenderState; |
88 | 89 | import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentMembershipDictionary; |
1005 | 1006 | @Override |
1006 | 1007 | public void drawImage(PDImage pdImage) throws IOException |
1007 | 1008 | { |
1009 | if (pdImage instanceof PDImageXObject && | |
1010 | isHiddenOCG(((PDImageXObject) pdImage).getOptionalContent())) | |
1011 | { | |
1012 | return; | |
1013 | } | |
1008 | 1014 | Matrix ctm = getGraphicsState().getCurrentTransformationMatrix(); |
1009 | 1015 | AffineTransform at = ctm.createAffineTransform(); |
1010 | 1016 | |
1218 | 1224 | |
1219 | 1225 | // prepare transfer functions (either one per color or one for all) |
1220 | 1226 | // and maps (actually arrays[256] to be faster) to avoid calculating values several times |
1221 | Integer rMap[]; | |
1222 | Integer gMap[]; | |
1223 | Integer bMap[]; | |
1227 | Integer[] rMap; | |
1228 | Integer[] gMap; | |
1229 | Integer[] bMap; | |
1224 | 1230 | PDFunction rf; |
1225 | 1231 | PDFunction gf; |
1226 | 1232 | PDFunction bf; |
1394 | 1400 | @Override |
1395 | 1401 | public void showForm(PDFormXObject form) throws IOException |
1396 | 1402 | { |
1403 | if (isHiddenOCG(form.getOptionalContent())) | |
1404 | { | |
1405 | return; | |
1406 | } | |
1397 | 1407 | if (isContentRendered()) |
1398 | 1408 | { |
1399 | 1409 | super.showForm(form); |
1403 | 1413 | @Override |
1404 | 1414 | public void showTransparencyGroup(PDTransparencyGroup form) throws IOException |
1405 | 1415 | { |
1416 | if (isHiddenOCG(form.getOptionalContent())) | |
1417 | { | |
1418 | return; | |
1419 | } | |
1406 | 1420 | if (!isContentRendered()) |
1407 | 1421 | { |
1408 | 1422 | return; |
670 | 670 | } |
671 | 671 | // test if our TextPosition starts after a new word would be expected to start |
672 | 672 | if (expectedStartOfNextWordX != EXPECTED_START_OF_NEXT_WORD_X_RESET_VALUE |
673 | && expectedStartOfNextWordX < positionX && | |
674 | // only bother adding a space if the last character was not a space | |
675 | lastPosition.getTextPosition().getUnicode() != null | |
676 | && !lastPosition.getTextPosition().getUnicode().endsWith(" ")) | |
673 | && expectedStartOfNextWordX < positionX | |
674 | // only bother adding a word separator if the last character was not a word separator | |
675 | && (wordSeparator.isEmpty() || // | |
676 | (lastPosition.getTextPosition().getUnicode() != null | |
677 | && !lastPosition.getTextPosition().getUnicode() | |
678 | .endsWith(wordSeparator)))) | |
677 | 679 | { |
678 | 680 | line.add(LineItem.getWordSeparator()); |
679 | 681 | } |
721 | 723 | |
722 | 724 | private boolean overlap(float y1, float height1, float y2, float height2) |
723 | 725 | { |
724 | return within(y1, y2, .1f) || y2 <= y1 && y2 >= y1 - height1 | |
725 | || y1 <= y2 && y1 >= y2 - height2; | |
726 | return within(y1, y2, .1f) || (y2 <= y1 && y1 - height1 - y2 < -(height1 * 0.1f)) | |
727 | || (y1 <= y2 && y2 - height2 - y1 < -(height2 * 0.1f)); | |
726 | 728 | } |
727 | 729 | |
728 | 730 | /** |
395 | 395 | + thisOperand[7] * otherOperand[5] |
396 | 396 | + thisOperand[8] * otherOperand[8]; |
397 | 397 | } |
398 | ||
398 | if (Float.isInfinite(result.single[0]) || Float.isNaN(result.single[0]) // | |
399 | || Float.isInfinite(result.single[1]) || Float.isNaN(result.single[1]) // | |
400 | || Float.isInfinite(result.single[2]) || Float.isNaN(result.single[2]) // | |
401 | || Float.isInfinite(result.single[3]) || Float.isNaN(result.single[3]) // | |
402 | || Float.isInfinite(result.single[4]) || Float.isNaN(result.single[4]) // | |
403 | || Float.isInfinite(result.single[5]) || Float.isNaN(result.single[5]) // | |
404 | || Float.isInfinite(result.single[6]) || Float.isNaN(result.single[6]) // | |
405 | || Float.isInfinite(result.single[7]) || Float.isNaN(result.single[7]) // | |
406 | || Float.isInfinite(result.single[8]) || Float.isNaN(result.single[8])) | |
407 | throw new IllegalArgumentException( | |
408 | "Multiplying two matrices produces illegal values"); | |
399 | 409 | return result; |
400 | 410 | } |
401 | 411 |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.pdfbox.util; | |
17 | ||
18 | import java.util.ArrayList; | |
19 | import java.util.Collection; | |
20 | import java.util.Collections; | |
21 | import java.util.LinkedHashSet; | |
22 | import java.util.List; | |
23 | import java.util.Map; | |
24 | import java.util.Set; | |
25 | ||
26 | /** | |
27 | * Map implementation with a smallest possible memory usage. | |
28 | * It should only be used for maps with small number of items | |
29 | * (e.g. <30) since most operations have an O(n) complexity. | |
30 | * Thus it should be used in cases with large number of map | |
31 | * objects, each having only few items. | |
32 | * | |
33 | * <p><code>null</code> is not supported for keys or values.</p> | |
34 | */ | |
35 | public class SmallMap<K, V> implements Map<K, V> | |
36 | { | |
37 | /** | |
38 | * stores key-value pair as 2 objects; key first; in case of empty map this might be <code>null</code> | |
39 | */ | |
40 | private Object[] mapArr; | |
41 | ||
42 | /** Creates empty map. */ | |
43 | public SmallMap() | |
44 | { | |
45 | } | |
46 | ||
47 | /** Creates map filled with entries from provided map. */ | |
48 | public SmallMap(Map<? extends K, ? extends V> initMap) | |
49 | { | |
50 | putAll(initMap); | |
51 | } | |
52 | ||
53 | /** | |
54 | * Returns index of key within map-array or <code>-1</code> | |
55 | * if key is not found (or key is <code>null</code>). | |
56 | */ | |
57 | private int findKey(Object key) | |
58 | { | |
59 | if (isEmpty() || (key==null)) | |
60 | { | |
61 | return -1; | |
62 | } | |
63 | ||
64 | for ( int aIdx = 0; aIdx < mapArr.length; aIdx+=2 ) | |
65 | { | |
66 | if (key.equals(mapArr[aIdx])) | |
67 | { | |
68 | return aIdx; | |
69 | } | |
70 | } | |
71 | ||
72 | return -1; | |
73 | } | |
74 | ||
75 | /** | |
76 | * Returns index of value within map-array or <code>-1</code> | |
77 | * if value is not found (or value is <code>null</code>). | |
78 | */ | |
79 | private int findValue(Object value) | |
80 | { | |
81 | if (isEmpty() || (value==null)) | |
82 | { | |
83 | return -1; | |
84 | } | |
85 | ||
86 | for ( int aIdx = 1; aIdx < mapArr.length; aIdx+=2 ) | |
87 | { | |
88 | if (value.equals(mapArr[aIdx])) | |
89 | { | |
90 | return aIdx; | |
91 | } | |
92 | } | |
93 | ||
94 | return -1; | |
95 | } | |
96 | ||
97 | @Override | |
98 | public int size() | |
99 | { | |
100 | return mapArr == null ? 0 : mapArr.length >> 1; | |
101 | } | |
102 | ||
103 | @Override | |
104 | public boolean isEmpty() | |
105 | { | |
106 | return (mapArr == null) || (mapArr.length == 0); | |
107 | } | |
108 | ||
109 | @Override | |
110 | public boolean containsKey(Object key) | |
111 | { | |
112 | return findKey(key) >= 0; | |
113 | } | |
114 | ||
115 | @Override | |
116 | public boolean containsValue(Object value) | |
117 | { | |
118 | return findValue(value) >= 0; | |
119 | } | |
120 | ||
121 | @SuppressWarnings("unchecked") | |
122 | @Override | |
123 | public V get(Object key) | |
124 | { | |
125 | int kIdx = findKey(key); | |
126 | ||
127 | return kIdx < 0 ? null : (V) mapArr[kIdx+1]; | |
128 | } | |
129 | ||
130 | @Override | |
131 | public V put(K key, V value) | |
132 | { | |
133 | if ((key == null) || (value == null)) | |
134 | { | |
135 | throw new NullPointerException( "Key or value must not be null."); | |
136 | } | |
137 | ||
138 | if (mapArr == null) | |
139 | { | |
140 | mapArr = new Object[] { key, value }; | |
141 | return null; | |
142 | } | |
143 | else | |
144 | { | |
145 | int kIdx = findKey(key); | |
146 | ||
147 | if (kIdx < 0) | |
148 | { | |
149 | // key unknown | |
150 | int oldLen = mapArr.length; | |
151 | Object[] newMapArr = new Object[oldLen+2]; | |
152 | System.arraycopy(mapArr, 0, newMapArr, 0, oldLen); | |
153 | newMapArr[oldLen] = key; | |
154 | newMapArr[oldLen+1] = value; | |
155 | mapArr = newMapArr; | |
156 | return null; | |
157 | } | |
158 | else | |
159 | { | |
160 | // key exists; replace value | |
161 | @SuppressWarnings("unchecked") | |
162 | V oldValue = (V) mapArr[kIdx+1]; | |
163 | mapArr[kIdx+1] = value; | |
164 | return oldValue; | |
165 | } | |
166 | } | |
167 | } | |
168 | ||
169 | @Override | |
170 | public V remove(Object key) | |
171 | { | |
172 | int kIdx = findKey(key); | |
173 | ||
174 | if (kIdx < 0) | |
175 | { | |
176 | // not found | |
177 | return null; | |
178 | } | |
179 | ||
180 | @SuppressWarnings("unchecked") | |
181 | V oldValue = (V) mapArr[kIdx+1]; | |
182 | int oldLen = mapArr.length; | |
183 | ||
184 | if (oldLen == 2) | |
185 | { | |
186 | // was last entry | |
187 | mapArr = null; | |
188 | } | |
189 | else | |
190 | { | |
191 | Object[] newMapArr = new Object[oldLen-2]; | |
192 | System.arraycopy(mapArr, 0, newMapArr, 0, kIdx); | |
193 | System.arraycopy(mapArr, kIdx+2, newMapArr, kIdx, oldLen - kIdx - 2); | |
194 | mapArr = newMapArr; | |
195 | } | |
196 | ||
197 | return oldValue; | |
198 | } | |
199 | ||
200 | @Override | |
201 | public final void putAll(Map<? extends K, ? extends V> otherMap) | |
202 | { | |
203 | if ((mapArr == null) || (mapArr.length == 0)) | |
204 | { | |
205 | // existing map is empty | |
206 | mapArr = new Object[otherMap.size() << 1]; | |
207 | int aIdx = 0; | |
208 | for (Entry<? extends K, ? extends V> entry : otherMap.entrySet()) | |
209 | { | |
210 | if ((entry.getKey() == null) || (entry.getValue() == null)) | |
211 | { | |
212 | throw new NullPointerException( "Key or value must not be null."); | |
213 | } | |
214 | ||
215 | mapArr[aIdx++] = entry.getKey(); | |
216 | mapArr[aIdx++] = entry.getValue(); | |
217 | } | |
218 | } | |
219 | else | |
220 | { | |
221 | int oldLen = mapArr.length; | |
222 | // first increase array size to hold all to put entries as if they have unknown keys | |
223 | // reduce after adding all to the required size | |
224 | Object[] newMapArr = new Object[oldLen+(otherMap.size() << 1)]; | |
225 | System.arraycopy(mapArr, 0, newMapArr, 0, oldLen); | |
226 | ||
227 | int newIdx = oldLen; | |
228 | for (Entry<? extends K, ? extends V> entry : otherMap.entrySet()) | |
229 | { | |
230 | if ((entry.getKey() == null) || (entry.getValue() == null)) | |
231 | { | |
232 | throw new NullPointerException( "Key or value must not be null."); | |
233 | } | |
234 | ||
235 | int existKeyIdx = findKey(entry.getKey()); | |
236 | ||
237 | if (existKeyIdx >= 0) | |
238 | { | |
239 | // existing key | |
240 | newMapArr[existKeyIdx+1] = entry.getValue(); | |
241 | } | |
242 | else | |
243 | { | |
244 | // new key | |
245 | newMapArr[newIdx++] = entry.getKey(); | |
246 | newMapArr[newIdx++] = entry.getValue(); | |
247 | } | |
248 | } | |
249 | ||
250 | if (newIdx < newMapArr.length) | |
251 | { | |
252 | Object[] reducedMapArr = new Object[newIdx]; | |
253 | System.arraycopy(newMapArr, 0, reducedMapArr, 0, newIdx); | |
254 | newMapArr = reducedMapArr; | |
255 | } | |
256 | ||
257 | mapArr = newMapArr; | |
258 | } | |
259 | } | |
260 | ||
261 | @Override | |
262 | public void clear() | |
263 | { | |
264 | mapArr = null; | |
265 | } | |
266 | ||
267 | /** | |
268 | * Returns a set view of the keys contained in this map. | |
269 | * | |
270 | * <p>The current implementation does not allow changes to the | |
271 | * returned key set (which would have to be reflected in the | |
272 | * underlying map.</p> | |
273 | */ | |
274 | @SuppressWarnings("unchecked") | |
275 | @Override | |
276 | public Set<K> keySet() | |
277 | { | |
278 | if (isEmpty()) | |
279 | { | |
280 | return Collections.emptySet(); | |
281 | } | |
282 | ||
283 | Set<K> keys = new LinkedHashSet<K>(); | |
284 | for (int kIdx = 0; kIdx < mapArr.length; kIdx+=2) | |
285 | { | |
286 | keys.add((K)mapArr[kIdx]); | |
287 | } | |
288 | return Collections.unmodifiableSet( keys ); | |
289 | } | |
290 | ||
291 | /** | |
292 | * Returns a collection of the values contained in this map. | |
293 | * | |
294 | * <p>The current implementation does not allow changes to the | |
295 | * returned collection (which would have to be reflected in the | |
296 | * underlying map.</p> | |
297 | */ | |
298 | @SuppressWarnings("unchecked") | |
299 | @Override | |
300 | public Collection<V> values() | |
301 | { | |
302 | if (isEmpty()) | |
303 | { | |
304 | return Collections.emptySet(); | |
305 | } | |
306 | ||
307 | List<V> values = new ArrayList<V>(mapArr.length >> 1); | |
308 | for (int vIdx = 1; vIdx < mapArr.length; vIdx+=2) | |
309 | { | |
310 | values.add((V)mapArr[vIdx]); | |
311 | } | |
312 | return Collections.unmodifiableList( values ); | |
313 | } | |
314 | ||
315 | private class SmallMapEntry implements Entry<K, V> | |
316 | { | |
317 | private final int keyIdx; | |
318 | ||
319 | SmallMapEntry(int keyInMapIdx) | |
320 | { | |
321 | keyIdx = keyInMapIdx; | |
322 | } | |
323 | ||
324 | @SuppressWarnings("unchecked") | |
325 | @Override | |
326 | public K getKey() | |
327 | { | |
328 | return (K)mapArr[keyIdx]; | |
329 | } | |
330 | ||
331 | @SuppressWarnings("unchecked") | |
332 | @Override | |
333 | public V getValue() | |
334 | { | |
335 | return (V)mapArr[keyIdx+1]; | |
336 | } | |
337 | ||
338 | @Override | |
339 | public V setValue(V value) | |
340 | { | |
341 | if (value == null) | |
342 | { | |
343 | throw new NullPointerException( "Key or value must not be null."); | |
344 | } | |
345 | ||
346 | V oldValue = getValue(); | |
347 | mapArr[keyIdx+1] = value; | |
348 | return oldValue; | |
349 | } | |
350 | ||
351 | @Override | |
352 | public int hashCode() | |
353 | { | |
354 | return getKey().hashCode(); | |
355 | } | |
356 | ||
357 | @Override | |
358 | public boolean equals(Object obj) | |
359 | { | |
360 | if (!(obj instanceof SmallMap.SmallMapEntry)) | |
361 | { | |
362 | return false; | |
363 | } | |
364 | @SuppressWarnings("unchecked") | |
365 | SmallMapEntry other = (SmallMapEntry) obj; | |
366 | ||
367 | return getKey().equals(other.getKey()) && getValue().equals(other.getValue()); | |
368 | } | |
369 | } | |
370 | ||
371 | @Override | |
372 | public Set<java.util.Map.Entry<K, V>> entrySet() | |
373 | { | |
374 | if (isEmpty()) | |
375 | { | |
376 | return Collections.emptySet(); | |
377 | } | |
378 | ||
379 | Set<java.util.Map.Entry<K, V>> entries = new LinkedHashSet<Entry<K, V>>(); | |
380 | for (int kIdx = 0; kIdx < mapArr.length; kIdx+=2) | |
381 | { | |
382 | entries.add(new SmallMapEntry(kIdx)); | |
383 | } | |
384 | return Collections.unmodifiableSet( entries ); | |
385 | } | |
386 | ||
387 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.pdfbox.cos; | |
17 | ||
18 | import static org.junit.Assert.assertFalse; | |
19 | import org.junit.Test; | |
20 | ||
21 | public class COSDictionaryTest | |
22 | { | |
23 | @Test | |
24 | public void testCOSDictionaryNotEqualsCOSStream() | |
25 | { | |
26 | COSDictionary cosDictionary = new COSDictionary(); | |
27 | COSStream cosStream = new COSStream(); | |
28 | cosDictionary.setItem(COSName.BE, COSName.BE); | |
29 | cosDictionary.setInt(COSName.LENGTH, 0); | |
30 | cosStream.setItem(COSName.BE, COSName.BE); | |
31 | assertFalse("a COSDictionary shall not be equal to a COSStream with the same dictionary entries", cosDictionary.equals(cosStream)); | |
32 | assertFalse("a COSStream shall not be equal to a COSDictionary with the same dictionary entries", cosStream.equals(cosDictionary)); | |
33 | } | |
34 | }⏎ |
52 | 52 | // Assert result |
53 | 53 | Assert.assertEquals(1, retval); |
54 | 54 | } |
55 | ||
56 | @Test | |
57 | public void checkHashCode() | |
58 | { | |
59 | // same object number 100 0 | |
60 | Assert.assertEquals(new COSObjectKey(100, 0).hashCode(), | |
61 | new COSObjectKey(100, 0).hashCode()); | |
62 | ||
63 | // different object numbers/same generation numbers 100 0 vs. 200 0 | |
64 | Assert.assertNotEquals(new COSObjectKey(100, 0).hashCode(), | |
65 | new COSObjectKey(200, 0).hashCode()); | |
66 | ||
67 | // different object numbers/different generation numbers/ sum of both numbers are equal 100 0 vs. 99 1 | |
68 | Assert.assertNotEquals(new COSObjectKey(100, 0).hashCode(), | |
69 | new COSObjectKey(99, 1).hashCode()); | |
70 | } | |
55 | 71 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.pdfbox.io; | |
17 | ||
18 | import java.io.IOException; | |
19 | import org.junit.Assert; | |
20 | ||
21 | import org.junit.Test; | |
22 | ||
23 | /** | |
24 | * Regression test to check the known bugs in {@link ScratchFileBuffer}. | |
25 | * | |
26 | * @author Kühn & Weyh Software GmbH | |
27 | */ | |
28 | public class ScratchFileBufferTest | |
29 | { | |
30 | ||
31 | private static final int PAGE_SIZE = 4096; | |
32 | private static final int NUM_ITERATIONS = 3; | |
33 | ||
34 | /** | |
35 | * PDFBOX-4756: test positions are correct when seeking and that no EOFException is thrown in | |
36 | * ScratchFileBuffer.seek() beyond last page. | |
37 | * | |
38 | * @throws IOException | |
39 | */ | |
40 | @Test | |
41 | public void testEOFBugInSeek() throws IOException | |
42 | { | |
43 | ScratchFile scratchFile = new ScratchFile(MemoryUsageSetting.setupTempFileOnly()); | |
44 | ScratchFileBuffer scratchFileBuffer = new ScratchFileBuffer(scratchFile); | |
45 | byte[] bytes = new byte[PAGE_SIZE]; | |
46 | for (int i = 0; i < NUM_ITERATIONS; i++) | |
47 | { | |
48 | long p0 = scratchFileBuffer.getPosition(); | |
49 | scratchFileBuffer.write(bytes); | |
50 | long p1 = scratchFileBuffer.getPosition(); | |
51 | Assert.assertEquals(PAGE_SIZE, p1 - p0); | |
52 | scratchFileBuffer.write(bytes); | |
53 | long p2 = scratchFileBuffer.getPosition(); | |
54 | Assert.assertEquals(PAGE_SIZE, p2 - p1); | |
55 | scratchFileBuffer.seek(0); | |
56 | scratchFileBuffer.seek(i * 2 * PAGE_SIZE); | |
57 | } | |
58 | scratchFile.close(); | |
59 | } | |
60 | } |
0 | /* | |
1 | * Copyright 2014 The Apache Software Foundation. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package org.apache.pdfbox.pdfparser; | |
17 | ||
18 | import java.io.ByteArrayInputStream; | |
19 | import java.io.IOException; | |
20 | import org.junit.Assert; | |
21 | import org.junit.Test; | |
22 | ||
23 | /** | |
24 | * Unittest for org.apache.pdfbox.pdfparser.InputStreamSource | |
25 | */ | |
26 | public class InputStreamSourceTest | |
27 | { | |
28 | @Test | |
29 | public void testPositionReadFully() throws IOException | |
30 | { | |
31 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
32 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
33 | ||
34 | InputStreamSource inputStreamSource = new InputStreamSource(bais); | |
35 | ||
36 | Assert.assertEquals(0, inputStreamSource.getPosition()); | |
37 | inputStreamSource.readFully(5); | |
38 | Assert.assertEquals(5, inputStreamSource.getPosition()); | |
39 | ||
40 | inputStreamSource.close(); | |
41 | } | |
42 | ||
43 | @Test | |
44 | public void testPositionRead() throws IOException | |
45 | { | |
46 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
47 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
48 | ||
49 | InputStreamSource inputStreamSource = new InputStreamSource(bais); | |
50 | ||
51 | Assert.assertEquals(0, inputStreamSource.getPosition()); | |
52 | inputStreamSource.read(); | |
53 | inputStreamSource.read(); | |
54 | inputStreamSource.read(); | |
55 | Assert.assertEquals(3, inputStreamSource.getPosition()); | |
56 | ||
57 | inputStreamSource.close(); | |
58 | } | |
59 | ||
60 | @Test | |
61 | public void testPositionReadBytes() throws IOException | |
62 | { | |
63 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
64 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
65 | ||
66 | InputStreamSource inputStreamSource = new InputStreamSource(bais); | |
67 | ||
68 | Assert.assertEquals(0, inputStreamSource.getPosition()); | |
69 | byte[] buffer = new byte[4]; | |
70 | inputStreamSource.read(buffer); | |
71 | Assert.assertEquals(4, inputStreamSource.getPosition()); | |
72 | ||
73 | inputStreamSource.read(buffer, 1, 2); | |
74 | Assert.assertEquals(6, inputStreamSource.getPosition()); | |
75 | ||
76 | inputStreamSource.close(); | |
77 | } | |
78 | ||
79 | @Test | |
80 | public void testPositionPeek() throws IOException | |
81 | { | |
82 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
83 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
84 | ||
85 | InputStreamSource inputStreamSource = new InputStreamSource(bais); | |
86 | ||
87 | Assert.assertEquals(0, inputStreamSource.getPosition()); | |
88 | inputStreamSource.readFully(6); | |
89 | Assert.assertEquals(6, inputStreamSource.getPosition()); | |
90 | ||
91 | inputStreamSource.peek(); | |
92 | Assert.assertEquals(6, inputStreamSource.getPosition()); | |
93 | ||
94 | inputStreamSource.close(); | |
95 | } | |
96 | ||
97 | @Test | |
98 | public void testPositionUnreadBytes() throws IOException | |
99 | { | |
100 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
101 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
102 | ||
103 | InputStreamSource inputStreamSource = new InputStreamSource(bais); | |
104 | ||
105 | Assert.assertEquals(0, inputStreamSource.getPosition()); | |
106 | inputStreamSource.read(); | |
107 | inputStreamSource.read(); | |
108 | byte[] readBytes = inputStreamSource.readFully(6); | |
109 | Assert.assertEquals(8, inputStreamSource.getPosition()); | |
110 | inputStreamSource.unread(readBytes); | |
111 | Assert.assertEquals(2, inputStreamSource.getPosition()); | |
112 | inputStreamSource.read(); | |
113 | Assert.assertEquals(3, inputStreamSource.getPosition()); | |
114 | inputStreamSource.read(readBytes, 2, 4); | |
115 | Assert.assertEquals(7, inputStreamSource.getPosition()); | |
116 | inputStreamSource.unread(readBytes, 2, 4); | |
117 | Assert.assertEquals(3, inputStreamSource.getPosition()); | |
118 | ||
119 | inputStreamSource.close(); | |
120 | } | |
121 | } |
0 | /* | |
1 | * Copyright 2014 The Apache Software Foundation. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package org.apache.pdfbox.pdfparser; | |
17 | ||
18 | import java.io.ByteArrayInputStream; | |
19 | import java.io.IOException; | |
20 | ||
21 | import org.apache.pdfbox.io.RandomAccessBuffer; | |
22 | import org.junit.Assert; | |
23 | import org.junit.Test; | |
24 | ||
25 | /** | |
26 | * Unittest for org.apache.pdfbox.pdfparser.RandomAccessSource | |
27 | */ | |
28 | public class RandomAccessSourceTest | |
29 | { | |
30 | @Test | |
31 | public void testPositionReadFully() throws IOException | |
32 | { | |
33 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
34 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
35 | ||
36 | RandomAccessSource randomAccessSource = new RandomAccessSource( | |
37 | new RandomAccessBuffer(bais)); | |
38 | ||
39 | Assert.assertEquals(0, randomAccessSource.getPosition()); | |
40 | randomAccessSource.readFully(5); | |
41 | Assert.assertEquals(5, randomAccessSource.getPosition()); | |
42 | ||
43 | randomAccessSource.close(); | |
44 | } | |
45 | ||
46 | @Test | |
47 | public void testPositionRead() throws IOException | |
48 | { | |
49 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
50 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
51 | ||
52 | RandomAccessSource randomAccessSource = new RandomAccessSource( | |
53 | new RandomAccessBuffer(bais)); | |
54 | ||
55 | Assert.assertEquals(0, randomAccessSource.getPosition()); | |
56 | randomAccessSource.read(); | |
57 | randomAccessSource.read(); | |
58 | randomAccessSource.read(); | |
59 | Assert.assertEquals(3, randomAccessSource.getPosition()); | |
60 | ||
61 | randomAccessSource.close(); | |
62 | } | |
63 | ||
64 | @Test | |
65 | public void testPositionReadBytes() throws IOException | |
66 | { | |
67 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
68 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
69 | ||
70 | RandomAccessSource randomAccessSource = new RandomAccessSource( | |
71 | new RandomAccessBuffer(bais)); | |
72 | ||
73 | Assert.assertEquals(0, randomAccessSource.getPosition()); | |
74 | byte[] buffer = new byte[4]; | |
75 | randomAccessSource.read(buffer); | |
76 | Assert.assertEquals(4, randomAccessSource.getPosition()); | |
77 | ||
78 | randomAccessSource.read(buffer, 1, 2); | |
79 | Assert.assertEquals(6, randomAccessSource.getPosition()); | |
80 | ||
81 | randomAccessSource.close(); | |
82 | } | |
83 | ||
84 | @Test | |
85 | public void testPositionPeek() throws IOException | |
86 | { | |
87 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
88 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
89 | ||
90 | RandomAccessSource randomAccessSource = new RandomAccessSource( | |
91 | new RandomAccessBuffer(bais)); | |
92 | ||
93 | Assert.assertEquals(0, randomAccessSource.getPosition()); | |
94 | randomAccessSource.readFully(6); | |
95 | Assert.assertEquals(6, randomAccessSource.getPosition()); | |
96 | ||
97 | randomAccessSource.peek(); | |
98 | Assert.assertEquals(6, randomAccessSource.getPosition()); | |
99 | ||
100 | randomAccessSource.close(); | |
101 | } | |
102 | ||
103 | @Test | |
104 | public void testPositionUnreadBytes() throws IOException | |
105 | { | |
106 | byte[] inputValues = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
107 | ByteArrayInputStream bais = new ByteArrayInputStream(inputValues); | |
108 | ||
109 | RandomAccessSource randomAccessSource = new RandomAccessSource( | |
110 | new RandomAccessBuffer(bais)); | |
111 | ||
112 | Assert.assertEquals(0, randomAccessSource.getPosition()); | |
113 | randomAccessSource.read(); | |
114 | randomAccessSource.read(); | |
115 | byte[] readBytes = randomAccessSource.readFully(6); | |
116 | Assert.assertEquals(8, randomAccessSource.getPosition()); | |
117 | randomAccessSource.unread(readBytes); | |
118 | Assert.assertEquals(2, randomAccessSource.getPosition()); | |
119 | randomAccessSource.read(); | |
120 | Assert.assertEquals(3, randomAccessSource.getPosition()); | |
121 | randomAccessSource.read(readBytes, 2, 4); | |
122 | Assert.assertEquals(7, randomAccessSource.getPosition()); | |
123 | randomAccessSource.unread(readBytes, 2, 4); | |
124 | Assert.assertEquals(3, randomAccessSource.getPosition()); | |
125 | ||
126 | randomAccessSource.close(); | |
127 | } | |
128 | } |
36 | 36 | import org.apache.pdfbox.io.ScratchFile; |
37 | 37 | import org.apache.pdfbox.pdmodel.PDDocument; |
38 | 38 | import org.apache.pdfbox.pdmodel.PDDocumentInformation; |
39 | import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; | |
40 | import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; | |
39 | 41 | import org.apache.pdfbox.rendering.PDFRenderer; |
40 | 42 | import org.apache.pdfbox.util.DateConverter; |
41 | 43 | import org.junit.Before; |
331 | 333 | PDDocument.load(new File(TARGETPDFDIR, "PDFBOX-4339.pdf")).close(); |
332 | 334 | } |
333 | 335 | |
336 | /** | |
337 | * Test parsing the "WXMDXCYRWFDCMOSFQJ5OAJIAFXYRZ5OA.pdf" file, which is susceptible to | |
338 | * regression. | |
339 | * | |
340 | * @throws IOException | |
341 | */ | |
342 | @Test | |
343 | public void testPDFBox4153() throws IOException | |
344 | { | |
345 | PDDocument doc = PDDocument.load(new File(TARGETPDFDIR, "PDFBOX-4153-WXMDXCYRWFDCMOSFQJ5OAJIAFXYRZ5OA.pdf")); | |
346 | PDDocumentOutline documentOutline = doc.getDocumentCatalog().getDocumentOutline(); | |
347 | PDOutlineItem firstChild = documentOutline.getFirstChild(); | |
348 | assertEquals("Main Menu", firstChild.getTitle()); | |
349 | doc.close(); | |
350 | } | |
351 | ||
352 | /** | |
353 | * Test that PDFBOX-4490 has 3 pages. | |
354 | * | |
355 | * @throws IOException | |
356 | */ | |
357 | @Test | |
358 | public void testPDFBox4490() throws IOException | |
359 | { | |
360 | PDDocument doc = PDDocument.load(new File(TARGETPDFDIR, "PDFBOX-4490.pdf")); | |
361 | assertEquals(3, doc.getNumberOfPages()); | |
362 | doc.close(); | |
363 | } | |
364 | ||
334 | 365 | private void executeParserTest(RandomAccessRead source, MemoryUsageSetting memUsageSetting) throws IOException |
335 | 366 | { |
336 | 367 | ScratchFile scratchFile = new ScratchFile(memUsageSetting); |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.pdfbox.pdfwriter; | |
17 | ||
18 | import java.awt.image.BufferedImage; | |
19 | import java.io.File; | |
20 | import java.io.IOException; | |
21 | import java.io.OutputStream; | |
22 | import javax.imageio.ImageIO; | |
23 | import org.apache.pdfbox.cos.COSName; | |
24 | import org.apache.pdfbox.pdfparser.PDFStreamParser; | |
25 | import org.apache.pdfbox.pdmodel.PDDocument; | |
26 | import org.apache.pdfbox.pdmodel.PDPage; | |
27 | import org.apache.pdfbox.pdmodel.common.PDStream; | |
28 | import org.apache.pdfbox.rendering.PDFRenderer; | |
29 | import org.apache.pdfbox.rendering.TestPDFToImage; | |
30 | import org.junit.After; | |
31 | import org.junit.AfterClass; | |
32 | import static org.junit.Assert.fail; | |
33 | import org.junit.Before; | |
34 | import org.junit.BeforeClass; | |
35 | import org.junit.Test; | |
36 | ||
37 | /** | |
38 | * | |
39 | * @author Tilman Hausherr | |
40 | */ | |
41 | public class ContentStreamWriterTest | |
42 | { | |
43 | ||
44 | private final File testDirIn = new File("target/test-output/contentstream/in"); | |
45 | private final File testDirOut = new File("target/test-output/contentstream/out"); | |
46 | ||
47 | public ContentStreamWriterTest() | |
48 | { | |
49 | testDirIn.mkdirs(); | |
50 | testDirOut.mkdirs(); | |
51 | } | |
52 | ||
53 | @BeforeClass | |
54 | public static void setUpClass() | |
55 | { | |
56 | } | |
57 | ||
58 | @AfterClass | |
59 | public static void tearDownClass() | |
60 | { | |
61 | } | |
62 | ||
63 | @Before | |
64 | public void setUp() | |
65 | { | |
66 | } | |
67 | ||
68 | @After | |
69 | public void tearDown() | |
70 | { | |
71 | } | |
72 | ||
73 | /** | |
74 | * Test parse content stream, write back tokens and compare rendering. | |
75 | * | |
76 | * @throws java.io.IOException | |
77 | */ | |
78 | @Test | |
79 | public void testPDFBox4750() throws IOException | |
80 | { | |
81 | String filename = "PDFBOX-4750.pdf"; | |
82 | File file = new File("target/pdfs", filename); | |
83 | PDDocument doc = PDDocument.load(file); | |
84 | ||
85 | PDFRenderer r = new PDFRenderer(doc); | |
86 | for (int i = 0; i < doc.getNumberOfPages(); ++i) | |
87 | { | |
88 | BufferedImage bim1 = r.renderImageWithDPI(i, 96); | |
89 | ImageIO.write(bim1, "png", new File(testDirIn, filename + "-" + (i + 1) + ".png")); | |
90 | PDPage page = doc.getPage(i); | |
91 | PDStream newContent = new PDStream(doc); | |
92 | ||
93 | PDFStreamParser parser = new PDFStreamParser(page); | |
94 | parser.parse(); | |
95 | OutputStream os = newContent.createOutputStream(COSName.FLATE_DECODE); | |
96 | ContentStreamWriter tokenWriter = new ContentStreamWriter(os); | |
97 | tokenWriter.writeTokens(parser.getTokens()); | |
98 | os.close(); | |
99 | ||
100 | page.setContents(newContent); | |
101 | } | |
102 | doc.save(new File(testDirIn, filename)); | |
103 | doc.close(); | |
104 | TestPDFToImage testPDFToImage = new TestPDFToImage(TestPDFToImage.class.getName()); | |
105 | if (!testPDFToImage.doTestFile(new File(testDirIn, filename), testDirIn.getAbsolutePath(), testDirOut.getAbsolutePath())) | |
106 | { | |
107 | fail("Rendering failed or is not identical, see in " + testDirOut); | |
108 | } | |
109 | } | |
110 | } |
15 | 15 | package org.apache.pdfbox.pdmodel.common; |
16 | 16 | |
17 | 17 | import static org.junit.Assert.assertEquals; |
18 | import static org.junit.Assert.assertFalse; | |
18 | 19 | import static org.junit.Assert.assertTrue; |
19 | 20 | |
21 | import java.io.File; | |
22 | import java.io.IOException; | |
20 | 23 | import java.util.ArrayList; |
24 | import java.util.Collections; | |
21 | 25 | import java.util.List; |
22 | 26 | |
23 | 27 | import org.apache.pdfbox.cos.COSArray; |
24 | 28 | import org.apache.pdfbox.cos.COSBase; |
25 | import org.apache.pdfbox.cos.COSName; | |
29 | import org.apache.pdfbox.pdmodel.PDDocument; | |
26 | 30 | import org.apache.pdfbox.pdmodel.PDPage; |
27 | 31 | import org.apache.pdfbox.pdmodel.interactive.annotation.AnnotationFilter; |
28 | 32 | import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; |
30 | 34 | import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationSquareCircle; |
31 | 35 | import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup; |
32 | 36 | import org.junit.Before; |
37 | import org.junit.Rule; | |
33 | 38 | import org.junit.Test; |
39 | import org.junit.rules.ExpectedException; | |
34 | 40 | |
35 | 41 | public class COSArrayListTest { |
42 | ||
43 | @Rule | |
44 | public ExpectedException thrown = ExpectedException.none(); | |
45 | ||
36 | 46 | // next two entries are to be used for comparison with |
37 | 47 | // COSArrayList behaviour in order to ensure that the |
38 | 48 | // intented object is now at the correct position. |
49 | 59 | // {@link PDPage.getAnnotations(AnnotationFilter annotationFilter)} |
50 | 60 | static PDPage pdPage; |
51 | 61 | |
62 | private static final File OUT_DIR = new File("target/test-output/pdmodel/common"); | |
63 | ||
52 | 64 | /* |
53 | 65 | * Create thre new different annotations an add them to the Java List/Array as |
54 | 66 | * well as PDFBox List/Array implementations. |
63 | 75 | annotationsList.add(txtMark); |
64 | 76 | annotationsList.add(txtLink); |
65 | 77 | annotationsList.add(aCircle); |
66 | assertTrue(annotationsList.size() == 3); | |
78 | annotationsList.add(txtLink); | |
79 | assertTrue(annotationsList.size() == 4); | |
67 | 80 | |
68 | 81 | tbcAnnotationsList = new ArrayList<PDAnnotation>(); |
69 | 82 | tbcAnnotationsList.add(txtMark); |
70 | 83 | tbcAnnotationsList.add(txtLink); |
71 | 84 | tbcAnnotationsList.add(aCircle); |
72 | assertTrue(tbcAnnotationsList.size() == 3); | |
85 | tbcAnnotationsList.add(txtLink); | |
86 | assertTrue(tbcAnnotationsList.size() == 4); | |
73 | 87 | |
74 | 88 | annotationsArray = new COSArray(); |
75 | 89 | annotationsArray.add(txtMark); |
76 | 90 | annotationsArray.add(txtLink); |
77 | 91 | annotationsArray.add(aCircle); |
78 | assertTrue(annotationsArray.size() == 3); | |
79 | ||
80 | tbcAnnotationsArray = new COSBase[3]; | |
92 | annotationsArray.add(txtLink); | |
93 | assertTrue(annotationsArray.size() == 4); | |
94 | ||
95 | tbcAnnotationsArray = new COSBase[4]; | |
81 | 96 | tbcAnnotationsArray[0] = txtMark.getCOSObject(); |
82 | 97 | tbcAnnotationsArray[1] = txtLink.getCOSObject(); |
83 | 98 | tbcAnnotationsArray[2] = aCircle.getCOSObject(); |
84 | assertTrue(tbcAnnotationsArray.length == 3); | |
99 | tbcAnnotationsArray[3] = txtLink.getCOSObject(); | |
100 | assertTrue(tbcAnnotationsArray.length == 4); | |
85 | 101 | |
86 | 102 | // add the annotations to the page |
87 | 103 | pdPage = new PDPage(); |
88 | 104 | pdPage.setAnnotations(annotationsList); |
105 | ||
106 | // create test output directory | |
107 | OUT_DIR.mkdirs(); | |
89 | 108 | } |
90 | 109 | |
91 | 110 | /** |
95 | 114 | public void getFromList() throws Exception { |
96 | 115 | COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); |
97 | 116 | |
98 | for (int i = 0; i < 3; i++) { | |
117 | for (int i = 0; i < cosArrayList.size(); i++) { | |
99 | 118 | PDAnnotation annot = (PDAnnotation) cosArrayList.get(i); |
100 | // compare position using COSArrayList | |
101 | assertTrue("PDAnnotations cosObject at " + i + " shall be at index " + i + " of COSArray", | |
102 | annotationsArray.indexOf(annot.getCOSObject()) == i); | |
119 | assertTrue("PDAnnotations cosObject at " + i + " shall be equal to index " + i + " of COSArray", | |
120 | annotationsArray.get(i).equals(annot.getCOSObject())); | |
103 | 121 | |
104 | 122 | // compare with Java List/Array |
105 | 123 | assertTrue("PDAnnotations at " + i + " shall be at index " + i + " of List", |
106 | tbcAnnotationsList.indexOf(annot) == i); | |
124 | tbcAnnotationsList.get(i).equals((annot))); | |
107 | 125 | assertEquals("PDAnnotations cosObject at " + i + " shall be at position " + i + " of Array", |
108 | tbcAnnotationsArray[i], annot.getCOSObject()); | |
126 | tbcAnnotationsArray[i], annot.getCOSObject()); | |
109 | 127 | } |
110 | 128 | } |
111 | 129 | |
120 | 138 | PDAnnotationSquareCircle aSquare = new PDAnnotationSquareCircle(PDAnnotationSquareCircle.SUB_TYPE_SQUARE); |
121 | 139 | cosArrayList.add(aSquare); |
122 | 140 | |
123 | assertTrue("List size shall be 4", annotationsList.size() == 4); | |
124 | assertTrue("COSArray size shall be 4", annotationsArray.size() == 4); | |
125 | ||
126 | PDAnnotation annot = (PDAnnotation) annotationsList.get(3); | |
127 | assertTrue("Added annotation shall be 4th entry in COSArray", annotationsArray.indexOf(annot.getCOSObject()) == 3); | |
128 | assertEquals("Provided COSArray and underlying COSArray shall be equal", annotationsArray, cosArrayList.toList()); | |
141 | assertTrue("List size shall be 5", annotationsList.size() == 5); | |
142 | assertTrue("COSArray size shall be 5", annotationsArray.size() == 5); | |
143 | ||
144 | PDAnnotation annot = (PDAnnotation) annotationsList.get(4); | |
145 | assertTrue("Added annotation shall be 4th entry in COSArray", annotationsArray.indexOf(annot.getCOSObject()) == 4); | |
146 | assertEquals("Provided COSArray and underlying COSArray shall be equal", annotationsArray, cosArrayList.getCOSArray()); | |
129 | 147 | } |
130 | 148 | |
131 | 149 | /** |
135 | 153 | public void removeFromListByIndex() throws Exception { |
136 | 154 | COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); |
137 | 155 | |
156 | int positionToRemove = 2; | |
157 | PDAnnotation toBeRemoved = cosArrayList.get(positionToRemove); | |
158 | ||
159 | assertEquals("Remove operation shall return the removed object",toBeRemoved, cosArrayList.remove(positionToRemove)); | |
160 | assertTrue("List size shall be 3", cosArrayList.size() == 3); | |
161 | assertTrue("COSArray size shall be 3", annotationsArray.size() == 3); | |
162 | ||
163 | assertTrue("PDAnnotation shall no longer exist in List", | |
164 | cosArrayList.indexOf(tbcAnnotationsList.get(positionToRemove)) == -1); | |
165 | assertTrue("COSObject shall no longer exist in COSArray", | |
166 | annotationsArray.indexOf(tbcAnnotationsArray[positionToRemove]) == -1); | |
167 | } | |
168 | ||
169 | /** | |
170 | * Test removing a unique PDModel element by index is in sync with underlying COSArray | |
171 | */ | |
172 | @Test | |
173 | public void removeUniqueFromListByObject() throws Exception { | |
174 | COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); | |
175 | ||
176 | int positionToRemove = 2; | |
177 | PDAnnotation toBeRemoved = annotationsList.get(positionToRemove); | |
178 | ||
179 | assertTrue("Remove operation shall return true",cosArrayList.remove(toBeRemoved)); | |
180 | assertTrue("List size shall be 3", cosArrayList.size() == 3); | |
181 | assertTrue("COSArray size shall be 3", annotationsArray.size() == 3); | |
182 | ||
183 | // compare with Java List/Array to ensure correct object at position | |
184 | assertTrue("List object at 3 is at position 2 in COSArrayList now", | |
185 | cosArrayList.get(2).equals(tbcAnnotationsList.get(3))); | |
186 | assertTrue("COSObject of List object at 3 is at position 2 in COSArray now", | |
187 | annotationsArray.get(2).equals(tbcAnnotationsList.get(3).getCOSObject())); | |
188 | assertTrue("Array object at 3 is at position 2 in underlying COSArray now", | |
189 | annotationsArray.get(2).equals(tbcAnnotationsArray[3])); | |
190 | ||
191 | assertTrue("PDAnnotation shall no longer exist in List", | |
192 | cosArrayList.indexOf(tbcAnnotationsList.get(positionToRemove)) == -1); | |
193 | assertTrue("COSObject shall no longer exist in COSArray", | |
194 | annotationsArray.indexOf(tbcAnnotationsArray[positionToRemove]) == -1); | |
195 | ||
196 | assertFalse("Remove shall not remove any object",cosArrayList.remove(toBeRemoved)); | |
197 | ||
198 | } | |
199 | ||
200 | /** | |
201 | * Test removing a unique PDModel element by index is in sync with underlying COSArray | |
202 | */ | |
203 | @Test | |
204 | public void removeAllUniqueFromListByObject() throws Exception { | |
205 | COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); | |
206 | ||
207 | int positionToRemove = 2; | |
208 | PDAnnotation toBeRemoved = annotationsList.get(positionToRemove); | |
209 | ||
210 | List<PDAnnotation> toBeRemovedInstances = Collections.singletonList(toBeRemoved); | |
211 | ||
212 | assertTrue("Remove operation shall return true",cosArrayList.removeAll(toBeRemovedInstances)); | |
213 | assertTrue("List size shall be 3", cosArrayList.size() == 3); | |
214 | assertTrue("COSArray size shall be 3", annotationsArray.size() == 3); | |
215 | ||
216 | assertFalse("Remove shall not remove any object",cosArrayList.removeAll(toBeRemovedInstances)); | |
217 | } | |
218 | ||
219 | ||
220 | /** | |
221 | * Test removing a multiple appearing PDModel element by index is in sync with underlying COSArray | |
222 | */ | |
223 | @Test | |
224 | public void removeMultipleFromListByObject() throws Exception { | |
225 | COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); | |
226 | ||
138 | 227 | int positionToRemove = 1; |
139 | cosArrayList.remove(positionToRemove); | |
140 | ||
228 | PDAnnotation toBeRemoved = tbcAnnotationsList.get(positionToRemove); | |
229 | ||
230 | assertTrue("Remove operation shall return true",cosArrayList.remove(toBeRemoved)); | |
231 | assertTrue("List size shall be 3", cosArrayList.size() == 3); | |
232 | assertTrue("COSArray size shall be 3", annotationsArray.size() == 3); | |
233 | ||
234 | assertTrue("Remove operation shall return true",cosArrayList.remove(toBeRemoved)); | |
141 | 235 | assertTrue("List size shall be 2", cosArrayList.size() == 2); |
142 | 236 | assertTrue("COSArray size shall be 2", annotationsArray.size() == 2); |
143 | 237 | |
144 | PDAnnotation annot = (PDAnnotation) cosArrayList.get(1); | |
145 | assertTrue("Object at original position 2 shall now be at position 1 in underlying COSArray", | |
146 | annotationsArray.indexOf(annot.getCOSObject()) == 1); | |
147 | ||
148 | // compare with Java List/Array to ensure correct object at position | |
149 | assertTrue("List object at 2 is at position 1 in COSArrayList now", | |
150 | cosArrayList.indexOf(tbcAnnotationsList.get(2)) == 1); | |
151 | assertTrue("COSObject of List object at 2 is at position 1 in COSArray now", | |
152 | annotationsArray.indexOf(tbcAnnotationsList.get(2).getCOSObject()) == 1); | |
153 | assertTrue("Array object at 2 is at position 1 in underlying COSArray now", | |
154 | annotationsArray.indexOf(tbcAnnotationsArray[2]) == 1); | |
155 | ||
156 | assertTrue("PDAnnotation shall no longer exist in List", | |
157 | cosArrayList.indexOf(tbcAnnotationsList.get(positionToRemove)) == -1); | |
158 | assertTrue("COSObject shall no longer exist in COSArray", | |
159 | annotationsArray.indexOf(tbcAnnotationsArray[positionToRemove]) == -1); | |
160 | } | |
161 | ||
162 | /** | |
163 | * Test removing a PDModel element by index is in sync with underlying COSArray | |
164 | */ | |
165 | @Test | |
166 | public void removeFromListByObject() throws Exception { | |
238 | } | |
239 | ||
240 | /** | |
241 | * Test removing a unique PDModel element by index is in sync with underlying COSArray | |
242 | */ | |
243 | @Test | |
244 | public void removeAllMultipleFromListByObject() throws Exception { | |
167 | 245 | COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); |
168 | 246 | |
169 | 247 | int positionToRemove = 1; |
170 | PDAnnotation toBeRemoved = tbcAnnotationsList.get(positionToRemove); | |
171 | ||
172 | cosArrayList.remove(toBeRemoved); | |
173 | ||
248 | PDAnnotation toBeRemoved = annotationsList.get(positionToRemove); | |
249 | ||
250 | List<PDAnnotation> toBeRemovedInstances = Collections.singletonList(toBeRemoved); | |
251 | ||
252 | assertTrue("Remove operation shall return true",cosArrayList.removeAll(toBeRemovedInstances)); | |
174 | 253 | assertTrue("List size shall be 2", cosArrayList.size() == 2); |
175 | 254 | assertTrue("COSArray size shall be 2", annotationsArray.size() == 2); |
176 | 255 | |
177 | PDAnnotation annot = (PDAnnotation) cosArrayList.get(1); | |
178 | assertTrue("Object at original position 2 shall now be at position 1 in underlying COSArray", | |
179 | annotationsArray.indexOf(annot.getCOSObject()) == 1); | |
180 | ||
181 | // compare with Java List/Array to ensure correct object at position | |
182 | assertTrue("List object at 2 is at position 1 in COSArrayList now", | |
183 | cosArrayList.indexOf(tbcAnnotationsList.get(2)) == 1); | |
184 | assertTrue("COSObject of List object at 2 is at position 1 in COSArray now", | |
185 | annotationsArray.indexOf(tbcAnnotationsList.get(2).getCOSObject()) == 1); | |
186 | assertTrue("Array object at 2 is at position 1 in underlying COSArray now", | |
187 | annotationsArray.indexOf(tbcAnnotationsArray[2]) == 1); | |
188 | ||
189 | assertTrue("PDAnnotation shall no longer exist in List", | |
190 | cosArrayList.indexOf(tbcAnnotationsList.get(positionToRemove)) == -1); | |
191 | assertTrue("COSObject shall no longer exist in COSArray", | |
192 | annotationsArray.indexOf(tbcAnnotationsArray[positionToRemove]) == -1); | |
256 | assertFalse("Remove shall not remove any object",cosArrayList.removeAll(toBeRemovedInstances)); | |
193 | 257 | } |
194 | 258 | |
195 | 259 | @Test |
196 | 260 | public void removeFromFilteredListByIndex() throws Exception |
197 | 261 | { |
262 | // removing from a filtered list is not permitted | |
263 | thrown.expect(UnsupportedOperationException.class); | |
264 | ||
198 | 265 | // retrieve all annotations from page but the link annotation |
199 | 266 | // which is 2nd in list - see above setup |
200 | 267 | AnnotationFilter annotsFilter = new AnnotationFilter() |
207 | 274 | }; |
208 | 275 | |
209 | 276 | COSArrayList<PDAnnotation> cosArrayList = (COSArrayList<PDAnnotation>) pdPage.getAnnotations(annotsFilter); |
210 | COSArray underlyingCOSArray = pdPage.getCOSObject().getCOSArray(COSName.ANNOTS); | |
211 | ||
212 | assertTrue("Filtered COSArrayList size shall be 2", cosArrayList.size() == 2); | |
213 | assertTrue("Underlying COSArray shall have 3 entries", underlyingCOSArray.size() == 3); | |
214 | assertTrue("Backed COSArray shall have 3 entries", cosArrayList.toList().size() == 3); | |
215 | ||
216 | // remove aCircle annotation | |
217 | int positionToRemove = 1; | |
218 | PDAnnotation toBeRemoved = cosArrayList.get(positionToRemove); | |
219 | assertTrue("We should remove the circle annotation", toBeRemoved.getSubtype().equals(PDAnnotationSquareCircle.SUB_TYPE_CIRCLE)); | |
220 | cosArrayList.remove(positionToRemove); | |
221 | ||
222 | assertTrue("List size shall be 2", cosArrayList.size() == 1); | |
223 | assertTrue("COSArray size shall be 2", underlyingCOSArray.size() == 2); | |
224 | assertTrue("Backed COSArray size shall be 2", cosArrayList.toList().size() == 2); | |
225 | ||
226 | assertTrue("Removed annotation shall no longer appear in COSArrayList", cosArrayList.indexOf(toBeRemoved) == -1); | |
227 | assertTrue("Removed annotation shall no longer appear in underlying COSArray", underlyingCOSArray.indexOf(toBeRemoved.getCOSObject()) == -1); | |
228 | assertTrue("Removed annotation shall no longer appear in backed COSArray", cosArrayList.toList().indexOf(toBeRemoved.getCOSObject()) == -1); | |
277 | ||
278 | // this call should fail | |
279 | cosArrayList.remove(1); | |
229 | 280 | } |
230 | 281 | |
231 | 282 | |
232 | 283 | @Test |
233 | 284 | public void removeFromFilteredListByObject() throws Exception |
234 | 285 | { |
286 | // removing from a filtered list is not permitted | |
287 | thrown.expect(UnsupportedOperationException.class); | |
288 | ||
235 | 289 | // retrieve all annotations from page but the link annotation |
236 | 290 | // which is 2nd in list - see above setup |
237 | 291 | AnnotationFilter annotsFilter = new AnnotationFilter() |
244 | 298 | }; |
245 | 299 | |
246 | 300 | COSArrayList<PDAnnotation> cosArrayList = (COSArrayList<PDAnnotation>) pdPage.getAnnotations(annotsFilter); |
247 | COSArray underlyingCOSArray = pdPage.getCOSObject().getCOSArray(COSName.ANNOTS); | |
248 | ||
249 | assertTrue("Filtered COSArrayList size shall be 2", cosArrayList.size() == 2); | |
250 | assertTrue("Underlying COSArray shall have 3 entries", underlyingCOSArray.size() == 3); | |
251 | assertTrue("Backed COSArray shall have 3 entries", cosArrayList.toList().size() == 3); | |
252 | ||
253 | // remove aCircle annotation | |
301 | ||
302 | // remove object | |
254 | 303 | int positionToRemove = 1; |
255 | 304 | PDAnnotation toBeRemoved = cosArrayList.get(positionToRemove); |
256 | assertTrue("We should remove the circle annotation", toBeRemoved.getSubtype().equals(PDAnnotationSquareCircle.SUB_TYPE_CIRCLE)); | |
305 | ||
306 | // this call should fail | |
257 | 307 | cosArrayList.remove(toBeRemoved); |
258 | 308 | |
259 | assertTrue("List size shall be 2", cosArrayList.size() == 1); | |
260 | assertTrue("COSArray size shall be 2", underlyingCOSArray.size() == 2); | |
261 | assertTrue("Backed COSArray size shall be 2", cosArrayList.toList().size() == 2); | |
262 | ||
263 | assertTrue("Removed annotation shall no longer appear in COSArrayList", cosArrayList.indexOf(toBeRemoved) == -1); | |
264 | assertTrue("Removed annotation shall no longer appear in underlying COSArray", underlyingCOSArray.indexOf(toBeRemoved.getCOSObject()) == -1); | |
265 | assertTrue("Removed annotation shall no longer appear in backed COSArray", cosArrayList.toList().indexOf(toBeRemoved.getCOSObject()) == -1); | |
309 | } | |
310 | ||
311 | @Test | |
312 | public void removeSingleDirectObject() throws IOException { | |
313 | ||
314 | // generate test file | |
315 | PDDocument pdf = new PDDocument(); | |
316 | ||
317 | PDPage page = new PDPage(); | |
318 | pdf.addPage(page); | |
319 | ||
320 | ArrayList<PDAnnotation> pageAnnots = new ArrayList<PDAnnotation>(); | |
321 | PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); | |
322 | PDAnnotationLink txtLink = new PDAnnotationLink(); | |
323 | ||
324 | // enforce the COSDictionaries to be written directly into the COSArray | |
325 | txtMark.getCOSObject().getCOSObject().setDirect(true); | |
326 | txtLink.getCOSObject().getCOSObject().setDirect(true); | |
327 | ||
328 | pageAnnots.add(txtMark); | |
329 | pageAnnots.add(txtMark); | |
330 | pageAnnots.add(txtMark); | |
331 | pageAnnots.add(txtLink); | |
332 | assertTrue("There shall be 4 annotations generated", pageAnnots.size() == 4); | |
333 | ||
334 | page.setAnnotations(pageAnnots); | |
335 | ||
336 | pdf.save(OUT_DIR + "/removeSingleDirectObjectTest.pdf"); | |
337 | pdf.close(); | |
338 | ||
339 | pdf = PDDocument.load(new File(OUT_DIR + "/removeSingleDirectObjectTest.pdf")); | |
340 | page = pdf.getPage(0); | |
341 | ||
342 | COSArrayList<PDAnnotation> annotations = (COSArrayList) page.getAnnotations(); | |
343 | ||
344 | assertTrue("There shall be 4 annotations retrieved", annotations.size() == 4); | |
345 | assertTrue("The size of the internal COSArray shall be 4", annotations.getCOSArray().size() == 4); | |
346 | ||
347 | PDAnnotation toBeRemoved = annotations.get(0); | |
348 | annotations.remove(toBeRemoved); | |
349 | ||
350 | assertTrue("There shall be 3 annotations left", annotations.size() == 3); | |
351 | assertTrue("The size of the internal COSArray shall be 3", annotations.getCOSArray().size() == 3); | |
352 | pdf.close(); | |
353 | } | |
354 | ||
355 | @Test | |
356 | public void removeSingleIndirectObject() throws IOException { | |
357 | ||
358 | // generate test file | |
359 | PDDocument pdf = new PDDocument(); | |
360 | PDPage page = new PDPage(); | |
361 | pdf.addPage(page); | |
362 | ||
363 | ArrayList<PDAnnotation> pageAnnots = new ArrayList<PDAnnotation>(); | |
364 | PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); | |
365 | PDAnnotationLink txtLink = new PDAnnotationLink(); | |
366 | ||
367 | pageAnnots.add(txtMark); | |
368 | pageAnnots.add(txtMark); | |
369 | pageAnnots.add(txtMark); | |
370 | pageAnnots.add(txtLink); | |
371 | assertTrue("There shall be 4 annotations generated", pageAnnots.size() == 4); | |
372 | ||
373 | page.setAnnotations(pageAnnots); | |
374 | ||
375 | pdf.save(OUT_DIR + "/removeSingleIndirectObjectTest.pdf"); | |
376 | pdf.close(); | |
377 | ||
378 | pdf = PDDocument.load(new File(OUT_DIR + "/removeSingleIndirectObjectTest.pdf")); | |
379 | page = pdf.getPage(0); | |
380 | ||
381 | COSArrayList<PDAnnotation> annotations = (COSArrayList) page.getAnnotations(); | |
382 | ||
383 | assertTrue("There shall be 4 annotations retrieved", annotations.size() == 4); | |
384 | assertTrue("The size of the internal COSArray shall be 4", annotations.getCOSArray().size() == 4); | |
385 | ||
386 | PDAnnotation toBeRemoved = annotations.get(0); | |
387 | ||
388 | annotations.remove(toBeRemoved); | |
389 | ||
390 | assertTrue("There shall be 3 annotations left", annotations.size() == 3); | |
391 | assertTrue("The size of the internal COSArray shall be 2", annotations.getCOSArray().size() == 3); | |
392 | pdf.close(); | |
393 | } | |
394 | ||
395 | // @Test | |
396 | // PDFBOX-4669, PDFBOX-4723 | |
397 | // This test is currently disabled with the removeAll function not properly | |
398 | // working. See the discussion in above mentioned tickets about currently not implementing equals which the | |
399 | // functionality would need to rely on. | |
400 | public void removeDirectObject() throws IOException { | |
401 | ||
402 | // generate test file | |
403 | PDDocument pdf = new PDDocument(); | |
404 | PDPage page = new PDPage(); | |
405 | pdf.addPage(page); | |
406 | ||
407 | ArrayList<PDAnnotation> pageAnnots = new ArrayList<PDAnnotation>(); | |
408 | PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); | |
409 | PDAnnotationLink txtLink = new PDAnnotationLink(); | |
410 | ||
411 | // enforce the COSDictionaries to be written directly into the COSArray | |
412 | txtMark.getCOSObject().getCOSObject().setDirect(true); | |
413 | txtLink.getCOSObject().getCOSObject().setDirect(true); | |
414 | ||
415 | pageAnnots.add(txtMark); | |
416 | pageAnnots.add(txtMark); | |
417 | pageAnnots.add(txtMark); | |
418 | pageAnnots.add(txtLink); | |
419 | assertTrue("There shall be 4 annotations generated", pageAnnots.size() == 4); | |
420 | ||
421 | page.setAnnotations(pageAnnots); | |
422 | ||
423 | pdf.save(OUT_DIR + "/removeDirectObjectTest.pdf"); | |
424 | pdf.close(); | |
425 | ||
426 | pdf = PDDocument.load(new File(OUT_DIR + "/removeDirectObjectTest.pdf")); | |
427 | page = pdf.getPage(0); | |
428 | ||
429 | COSArrayList<PDAnnotation> annotations = (COSArrayList) page.getAnnotations(); | |
430 | ||
431 | assertTrue("There shall be 4 annotations retrieved", annotations.size() == 4); | |
432 | assertTrue("The size of the internal COSArray shall be 4", annotations.getCOSArray().size() == 4); | |
433 | ||
434 | ArrayList<PDAnnotation> toBeRemoved = new ArrayList<PDAnnotation>(); | |
435 | ||
436 | toBeRemoved.add(annotations.get(0)); | |
437 | annotations.removeAll(toBeRemoved); | |
438 | ||
439 | assertTrue("There shall be 1 annotations left", annotations.size() == 1); | |
440 | assertTrue("The size of the internal COSArray shall be 1", annotations.getCOSArray().size() == 1); | |
441 | } | |
442 | ||
443 | @Test | |
444 | public void removeIndirectObject() throws IOException { | |
445 | ||
446 | // generate test file | |
447 | PDDocument pdf = new PDDocument(); | |
448 | PDPage page = new PDPage(); | |
449 | pdf.addPage(page); | |
450 | ||
451 | ArrayList<PDAnnotation> pageAnnots = new ArrayList<PDAnnotation>(); | |
452 | PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); | |
453 | PDAnnotationLink txtLink = new PDAnnotationLink(); | |
454 | ||
455 | pageAnnots.add(txtMark); | |
456 | pageAnnots.add(txtMark); | |
457 | pageAnnots.add(txtMark); | |
458 | pageAnnots.add(txtLink); | |
459 | assertTrue("There shall be 4 annotations generated", pageAnnots.size() == 4); | |
460 | ||
461 | page.setAnnotations(pageAnnots); | |
462 | ||
463 | pdf.save(OUT_DIR + "/removeIndirectObjectTest.pdf"); | |
464 | pdf.close(); | |
465 | ||
466 | pdf = PDDocument.load(new File(OUT_DIR + "/removeIndirectObjectTest.pdf")); | |
467 | page = pdf.getPage(0); | |
468 | ||
469 | COSArrayList<PDAnnotation> annotations = (COSArrayList) page.getAnnotations(); | |
470 | ||
471 | assertTrue("There shall be 4 annotations retrieved", annotations.size() == 4); | |
472 | assertTrue("The size of the internal COSArray shall be 4", annotations.getCOSArray().size() == 4); | |
473 | ||
474 | ArrayList<PDAnnotation> toBeRemoved = new ArrayList<PDAnnotation>(); | |
475 | toBeRemoved.add(annotations.get(0)); | |
476 | ||
477 | annotations.removeAll(toBeRemoved); | |
478 | ||
479 | assertTrue("There shall be 1 annotations left", annotations.size() == 1); | |
480 | assertTrue("The size of the internal COSArray shall be 1", annotations.getCOSArray().size() == 1); | |
481 | } | |
482 | ||
483 | // @Test | |
484 | // PDFBOX-4669, PDFBOX-4723 | |
485 | // This test is currently disabled with the retainAll function not properly | |
486 | // working. See the discussion in above mentioned tickets about currently not implementing equals which the | |
487 | // functionality would need to rely on. | |
488 | public void retainDirectObject() throws IOException { | |
489 | ||
490 | // generate test file | |
491 | PDDocument pdf = new PDDocument(); | |
492 | PDPage page = new PDPage(); | |
493 | pdf.addPage(page); | |
494 | ||
495 | ArrayList<PDAnnotation> pageAnnots = new ArrayList<PDAnnotation>(); | |
496 | PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); | |
497 | PDAnnotationLink txtLink = new PDAnnotationLink(); | |
498 | ||
499 | // enforce the COSDictionaries to be written directly into the COSArray | |
500 | txtMark.getCOSObject().getCOSObject().setDirect(true); | |
501 | txtLink.getCOSObject().getCOSObject().setDirect(true); | |
502 | ||
503 | pageAnnots.add(txtMark); | |
504 | pageAnnots.add(txtMark); | |
505 | pageAnnots.add(txtMark); | |
506 | pageAnnots.add(txtLink); | |
507 | assertTrue("There shall be 4 annotations generated", pageAnnots.size() == 4); | |
508 | ||
509 | page.setAnnotations(pageAnnots); | |
510 | ||
511 | pdf.save(OUT_DIR + "/retainDirectObjectTest.pdf"); | |
512 | pdf.close(); | |
513 | ||
514 | pdf = PDDocument.load(new File(OUT_DIR + "/retainDirectObjectTest.pdf")); | |
515 | page = pdf.getPage(0); | |
516 | ||
517 | COSArrayList<PDAnnotation> annotations = (COSArrayList) page.getAnnotations(); | |
518 | ||
519 | assertTrue("There shall be 4 annotations retrieved", annotations.size() == 4); | |
520 | assertTrue("The size of the internal COSArray shall be 4", annotations.getCOSArray().size() == 4); | |
521 | ||
522 | ArrayList<PDAnnotation> toBeRetained = new ArrayList<PDAnnotation>(); | |
523 | ||
524 | toBeRetained.add(annotations.get(0)); | |
525 | annotations.retainAll(toBeRetained); | |
526 | ||
527 | assertTrue("There shall be 3 annotations left", annotations.size() == 3); | |
528 | assertTrue("The size of the internal COSArray shall be 3", annotations.getCOSArray().size() == 3); | |
529 | } | |
530 | ||
531 | // @Test | |
532 | // PDFBOX-4669, PDFBOX-4723 | |
533 | // This test is currently disabled with the retainAll function not properly | |
534 | // working. See the discussion in above mentioned tickets about currently not implementing equals which the | |
535 | // functionality would need to rely on. | |
536 | public void retainIndirectObject() throws IOException { | |
537 | ||
538 | // generate test file | |
539 | PDDocument pdf = new PDDocument(); | |
540 | PDPage page = new PDPage(); | |
541 | pdf.addPage(page); | |
542 | ||
543 | ArrayList<PDAnnotation> pageAnnots = new ArrayList<PDAnnotation>(); | |
544 | PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); | |
545 | PDAnnotationLink txtLink = new PDAnnotationLink(); | |
546 | ||
547 | // enforce the COSDictionaries to be written directly into the COSArray | |
548 | txtMark.getCOSObject().getCOSObject().setDirect(true); | |
549 | txtLink.getCOSObject().getCOSObject().setDirect(true); | |
550 | ||
551 | pageAnnots.add(txtMark); | |
552 | pageAnnots.add(txtMark); | |
553 | pageAnnots.add(txtMark); | |
554 | pageAnnots.add(txtLink); | |
555 | assertTrue("There shall be 4 annotations generated", pageAnnots.size() == 4); | |
556 | ||
557 | page.setAnnotations(pageAnnots); | |
558 | ||
559 | pdf.save(OUT_DIR + "/retainIndirectObjectTest.pdf"); | |
560 | pdf.close(); | |
561 | ||
562 | pdf = PDDocument.load(new File(OUT_DIR + "/retainIndirectObjectTest.pdf")); | |
563 | page = pdf.getPage(0); | |
564 | ||
565 | COSArrayList<PDAnnotation> annotations = (COSArrayList) page.getAnnotations(); | |
566 | ||
567 | assertTrue("There shall be 4 annotations retrieved", annotations.size() == 4); | |
568 | assertTrue("The size of the internal COSArray shall be 4", annotations.getCOSArray().size() == 4); | |
569 | ||
570 | ArrayList<PDAnnotation> toBeRetained = new ArrayList<PDAnnotation>(); | |
571 | ||
572 | toBeRetained.add(annotations.get(0)); | |
573 | annotations.retainAll(toBeRetained); | |
574 | ||
575 | assertTrue("There shall be 3 annotations left", annotations.size() == 3); | |
576 | assertTrue("The size of the internal COSArray shall be 3", annotations.getCOSArray().size() == 3); | |
266 | 577 | } |
267 | 578 | }⏎ |
+21
-21
31 | 31 | import org.apache.pdfbox.pdmodel.PDDocument; |
32 | 32 | import org.apache.pdfbox.rendering.PDFRenderer; |
33 | 33 | import org.apache.pdfbox.rendering.TestPDFToImage; |
34 | import static org.junit.Assert.fail; | |
34 | 35 | import org.junit.Before; |
35 | 36 | import org.junit.Test; |
36 | 37 | |
37 | 38 | /** |
38 | 39 | * Test flatten different forms and compare with rendering. |
39 | 40 | * |
40 | * The tests are currently disabled to not run within the CI environment | |
41 | * Some of the tests are currently disabled to not run within the CI environment | |
41 | 42 | * as the test results need manual inspection. Enable as needed. |
42 | 43 | * |
43 | 44 | */ |
57 | 58 | /* |
58 | 59 | * PDFBOX-142 Filled template. |
59 | 60 | */ |
60 | //@Test | |
61 | // @Test | |
61 | 62 | public void testFlattenPDFBOX142() throws IOException |
62 | 63 | { |
63 | 64 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12742551/Testformular1.pdf"; |
69 | 70 | /* |
70 | 71 | * PDFBOX-563 Filled template. |
71 | 72 | */ |
72 | //@Test | |
73 | @Test | |
73 | 74 | public void testFlattenPDFBOX563() throws IOException |
74 | 75 | { |
75 | 76 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12425859/TestFax_56972.pdf"; |
81 | 82 | /* |
82 | 83 | * PDFBOX-2469 Empty template. |
83 | 84 | */ |
84 | //@Test | |
85 | @Test | |
85 | 86 | public void testFlattenPDFBOX2469Empty() throws IOException |
86 | 87 | { |
87 | 88 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12682897/FormI-9-English.pdf"; |
93 | 94 | /* |
94 | 95 | * PDFBOX-2469 Filled template. |
95 | 96 | */ |
96 | //@Test | |
97 | @Test | |
97 | 98 | public void testFlattenPDFBOX2469Filled() throws IOException |
98 | 99 | { |
99 | 100 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12678455/testPDF_acroForm.pdf"; |
105 | 106 | /* |
106 | 107 | * PDFBOX-2586 Empty template. |
107 | 108 | */ |
108 | //@Test | |
109 | @Test | |
109 | 110 | public void testFlattenPDFBOX2586() throws IOException |
110 | 111 | { |
111 | 112 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12689788/test.pdf"; |
117 | 118 | /* |
118 | 119 | * PDFBOX-3083 Filled template rotated. |
119 | 120 | */ |
120 | //@Test | |
121 | // @Test | |
121 | 122 | public void testFlattenPDFBOX3083() throws IOException |
122 | 123 | { |
123 | 124 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12770263/mypdf.pdf"; |
129 | 130 | /* |
130 | 131 | * PDFBOX-3262 Hidden fields |
131 | 132 | */ |
132 | //@Test | |
133 | @Test | |
133 | 134 | public void testFlattenPDFBOX3262() throws IOException |
134 | 135 | { |
135 | 136 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12792007/hidden_fields.pdf"; |
141 | 142 | /* |
142 | 143 | * PDFBOX-3396 Signed Document 1. |
143 | 144 | */ |
144 | //@Test | |
145 | @Test | |
145 | 146 | public void testFlattenPDFBOX3396_1() throws IOException |
146 | 147 | { |
147 | 148 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12816014/Signed-Document-1.pdf"; |
153 | 154 | /* |
154 | 155 | * PDFBOX-3396 Signed Document 2. |
155 | 156 | */ |
156 | //@Test | |
157 | @Test | |
157 | 158 | public void testFlattenPDFBOX3396_2() throws IOException |
158 | 159 | { |
159 | 160 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12816016/Signed-Document-2.pdf"; |
165 | 166 | /* |
166 | 167 | * PDFBOX-3396 Signed Document 3. |
167 | 168 | */ |
168 | //@Test | |
169 | @Test | |
169 | 170 | public void testFlattenPDFBOX3396_3() throws IOException |
170 | 171 | { |
171 | 172 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12821307/Signed-Document-3.pdf"; |
177 | 178 | /* |
178 | 179 | * PDFBOX-3396 Signed Document 4. |
179 | 180 | */ |
180 | //@Test | |
181 | @Test | |
181 | 182 | public void testFlattenPDFBOX3396_4() throws IOException |
182 | 183 | { |
183 | 184 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12821308/Signed-Document-4.pdf"; |
189 | 190 | /* |
190 | 191 | * PDFBOX-3587 Empty template. |
191 | 192 | */ |
192 | //@Test | |
193 | // @Test | |
193 | 194 | public void testFlattenOpenOfficeForm() throws IOException |
194 | 195 | { |
195 | 196 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12839977/OpenOfficeForm.pdf"; |
201 | 202 | /* |
202 | 203 | * PDFBOX-3587 Filled template. |
203 | 204 | */ |
204 | //@Test | |
205 | // @Test | |
205 | 206 | public void testFlattenOpenOfficeFormFilled() throws IOException |
206 | 207 | { |
207 | 208 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12840280/OpenOfficeForm_filled.pdf"; |
213 | 214 | /** |
214 | 215 | * PDFBOX-4157 Filled template. |
215 | 216 | */ |
216 | //@Test | |
217 | // @Test | |
217 | 218 | public void testFlattenPDFBox4157() throws IOException |
218 | 219 | { |
219 | 220 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12976553/PDFBOX-4157-filled.pdf"; |
225 | 226 | /** |
226 | 227 | * PDFBOX-4172 Filled template. |
227 | 228 | */ |
228 | //@Test | |
229 | // @Test | |
229 | 230 | public void testFlattenPDFBox4172() throws IOException |
230 | 231 | { |
231 | 232 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12976552/PDFBOX-4172-filled.pdf"; |
237 | 238 | /** |
238 | 239 | * PDFBOX-4615 Filled template. |
239 | 240 | */ |
240 | //@Test | |
241 | // @Test | |
241 | 242 | public void testFlattenPDFBox4615() throws IOException |
242 | 243 | { |
243 | 244 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12976452/resetboundingbox-filled.pdf"; |
249 | 250 | /** |
250 | 251 | * PDFBOX-4693: page is not rotated, but the appearance stream is. |
251 | 252 | */ |
252 | //@Test | |
253 | @Test | |
253 | 254 | public void testFlattenPDFBox4693() throws IOException |
254 | 255 | { |
255 | 256 | String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12986337/stenotypeTest-3_rotate_no_flatten.pdf"; |
280 | 281 | TestPDFToImage testPDFToImage = new TestPDFToImage(TestPDFToImage.class.getName()); |
281 | 282 | if (!testPDFToImage.doTestFile(outputFile, IN_DIR.getAbsolutePath(), OUT_DIR.getAbsolutePath())) |
282 | 283 | { |
283 | // don't fail, rendering is different on different systems, result must be viewed manually | |
284 | System.out.println("Rendering of " + outputFile + " failed or is not identical to expected rendering in " + IN_DIR + " directory"); | |
284 | fail("Rendering of " + outputFile + " failed or is not identical to expected rendering in " + IN_DIR + " directory"); | |
285 | 285 | removeMatchingRenditions(inputFile); |
286 | 286 | return false; |
287 | 287 | } |
359 | 359 | |
360 | 360 | for (File testFile : testFiles) |
361 | 361 | { |
362 | if (! new File(OUT_DIR, testFile.getName()).exists()) | |
362 | if (!new File(OUT_DIR, testFile.getName()).exists()) | |
363 | 363 | { |
364 | 364 | testFile.delete(); |
365 | 365 | } |
184 | 184 | { |
185 | 185 | equals = false; |
186 | 186 | log.warn("Actual line is longer at:" + actualIndex ); |
187 | } | |
188 | if (expectedArray.length != actualArray.length) | |
189 | { | |
190 | equals = false; | |
191 | log.warn("Expected lines: " + expectedArray.length + ", actual lines: " + actualArray.length); | |
187 | 192 | } |
188 | 193 | } |
189 | 194 | } |
21 | 21 | |
22 | 22 | /** |
23 | 23 | * |
24 | * @author Neil McErlean | |
24 | 25 | * @author Tilman Hausherr |
25 | 26 | */ |
26 | 27 | public class MatrixTest |
27 | 28 | { |
28 | 29 | |
30 | @Test | |
31 | public void testConstructionAndCopy() throws Exception | |
32 | { | |
33 | Matrix m1 = new Matrix(); | |
34 | assertMatrixIsPristine(m1); | |
35 | ||
36 | Matrix m2 = m1.clone(); | |
37 | assertNotSame(m1, m2); | |
38 | assertMatrixIsPristine(m2); | |
39 | } | |
40 | ||
41 | @Test | |
42 | public void testMultiplication() throws Exception | |
43 | { | |
44 | // This matrix will not change - we use it to drive the various multiplications. | |
45 | final Matrix testMatrix = new Matrix(); | |
46 | ||
47 | // Create matrix with values | |
48 | // [ 0, 1, 2 | |
49 | // 1, 2, 3 | |
50 | // 2, 3, 4] | |
51 | for (int x = 0; x < 3; x++) | |
52 | { | |
53 | for (int y = 0; y < 3; y++) | |
54 | { | |
55 | testMatrix.setValue(x, y, x + y); | |
56 | } | |
57 | } | |
58 | ||
59 | Matrix m1 = testMatrix.clone(); | |
60 | Matrix m2 = testMatrix.clone(); | |
61 | ||
62 | // Multiply two matrices together producing a new result matrix. | |
63 | Matrix product = m1.multiply(m2); | |
64 | ||
65 | assertNotSame(m1, product); | |
66 | assertNotSame(m2, product); | |
67 | ||
68 | // Operand 1 should not have changed | |
69 | assertMatrixValuesEqualTo(new float[] { 0, 1, 2, 1, 2, 3, 2, 3, 4 }, m1); | |
70 | // Operand 2 should not have changed | |
71 | assertMatrixValuesEqualTo(new float[] { 0, 1, 2, 1, 2, 3, 2, 3, 4 }, m2); | |
72 | assertMatrixValuesEqualTo(new float[] { 5, 8, 11, 8, 14, 20, 11, 20, 29 }, product); | |
73 | ||
74 | // Multiply two matrices together with the result being written to a third matrix | |
75 | // (Any existing values there will be overwritten). | |
76 | Matrix resultMatrix = new Matrix(); | |
77 | ||
78 | Matrix retVal = m1.multiply(m2, resultMatrix); | |
79 | assertSame(retVal, resultMatrix); | |
80 | // Operand 1 should not have changed | |
81 | assertMatrixValuesEqualTo(new float[] { 0, 1, 2, 1, 2, 3, 2, 3, 4 }, m1); | |
82 | // Operand 2 should not have changed | |
83 | assertMatrixValuesEqualTo(new float[] { 0, 1, 2, 1, 2, 3, 2, 3, 4 }, m2); | |
84 | assertMatrixValuesEqualTo(new float[] { 5, 8, 11, 8, 14, 20, 11, 20, 29 }, resultMatrix); | |
85 | ||
86 | // Multiply two matrices together with the result being written into the other matrix | |
87 | retVal = m1.multiply(m2, m2); | |
88 | assertSame(retVal, m2); | |
89 | // Operand 1 should not have changed | |
90 | assertMatrixValuesEqualTo(new float[] { 0, 1, 2, 1, 2, 3, 2, 3, 4 }, m1); | |
91 | assertMatrixValuesEqualTo(new float[] { 5, 8, 11, 8, 14, 20, 11, 20, 29 }, retVal); | |
92 | ||
93 | // Multiply two matrices together with the result being written into 'this' matrix | |
94 | m1 = testMatrix.clone(); | |
95 | m2 = testMatrix.clone(); | |
96 | ||
97 | retVal = m1.multiply(m2, m1); | |
98 | assertSame(retVal, m1); | |
99 | // Operand 2 should not have changed | |
100 | assertMatrixValuesEqualTo(new float[] { 0, 1, 2, 1, 2, 3, 2, 3, 4 }, m2); | |
101 | assertMatrixValuesEqualTo(new float[] { 5, 8, 11, 8, 14, 20, 11, 20, 29 }, retVal); | |
102 | ||
103 | // Multiply the same matrix with itself with the result being written into 'this' matrix | |
104 | m1 = testMatrix.clone(); | |
105 | ||
106 | retVal = m1.multiply(m1, m1); | |
107 | assertSame(retVal, m1); | |
108 | assertMatrixValuesEqualTo(new float[] { 5, 8, 11, 8, 14, 20, 11, 20, 29 }, retVal); | |
109 | } | |
110 | ||
111 | @Test(expected = IllegalArgumentException.class) | |
112 | public void testIllegalValueNaN1() | |
113 | { | |
114 | Matrix m = new Matrix(); | |
115 | m.setValue(0, 0, Float.MAX_VALUE); | |
116 | m.multiply(m, m); | |
117 | } | |
118 | ||
119 | @Test(expected = IllegalArgumentException.class) | |
120 | public void testIllegalValueNaN2() | |
121 | { | |
122 | Matrix m = new Matrix(); | |
123 | m.setValue(0, 0, Float.NaN); | |
124 | m.multiply(m, m); | |
125 | } | |
126 | ||
127 | @Test(expected = IllegalArgumentException.class) | |
128 | public void testIllegalValuePositiveInfinity() | |
129 | { | |
130 | Matrix m = new Matrix(); | |
131 | m.setValue(0, 0, Float.POSITIVE_INFINITY); | |
132 | m.multiply(m, m); | |
133 | } | |
134 | ||
135 | @Test(expected = IllegalArgumentException.class) | |
136 | public void testIllegalValueNegativeInfinity() | |
137 | { | |
138 | Matrix m = new Matrix(); | |
139 | m.setValue(0, 0, Float.NEGATIVE_INFINITY); | |
140 | m.multiply(m, m); | |
141 | } | |
142 | ||
29 | 143 | /** |
30 | 144 | * Test of PDFBOX-2872 bug |
31 | 145 | */ |
43 | 157 | |
44 | 158 | } |
45 | 159 | |
160 | /** | |
161 | * This method asserts that the matrix values for the given {@link Matrix} object are equal to the pristine, or | |
162 | * original, values. | |
163 | * | |
164 | * @param m the Matrix to test. | |
165 | */ | |
166 | private void assertMatrixIsPristine(Matrix m) | |
167 | { | |
168 | assertMatrixValuesEqualTo(new float[] { 1, 0, 0, 0, 1, 0, 0, 0, 1 }, m); | |
169 | } | |
170 | ||
171 | /** | |
172 | * This method asserts that the matrix values for the given {@link Matrix} object have the specified values. | |
173 | * | |
174 | * @param values the expected values | |
175 | * @param m the matrix to test | |
176 | */ | |
177 | private void assertMatrixValuesEqualTo(float[] values, Matrix m) | |
178 | { | |
179 | float delta = 0.00001f; | |
180 | for (int i = 0; i < values.length; i++) | |
181 | { | |
182 | // Need to convert a (row, column) coordinate into a straight index. | |
183 | int row = (int) Math.floor(i / 3); | |
184 | int column = i % 3; | |
185 | StringBuilder failureMsg = new StringBuilder(); | |
186 | failureMsg.append("Incorrect value for matrix[").append(row).append(",").append(column) | |
187 | .append("]"); | |
188 | assertEquals(failureMsg.toString(), values[i], m.getValue(row, column), delta); | |
189 | } | |
190 | } | |
46 | 191 | |
47 | 192 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.pdfbox.util; | |
17 | ||
18 | import java.io.IOException; | |
19 | ||
20 | import junit.framework.Test; | |
21 | import junit.framework.TestCase; | |
22 | import junit.framework.TestSuite; | |
23 | ||
24 | /** | |
25 | * Test the {@link Matrix} class. | |
26 | * @author Neil McErlean | |
27 | * @since 1.4.0 | |
28 | */ | |
29 | public class TestMatrix extends TestCase | |
30 | { | |
31 | /** | |
32 | * Test class constructor. | |
33 | * | |
34 | * @param name The name of the test class. | |
35 | * | |
36 | * @throws IOException If there is an error creating the test. | |
37 | */ | |
38 | public TestMatrix( String name ) throws IOException | |
39 | { | |
40 | super( name ); | |
41 | } | |
42 | ||
43 | public void testConstructionAndCopy() throws Exception | |
44 | { | |
45 | Matrix m1 = new Matrix(); | |
46 | assertMatrixIsPristine(m1); | |
47 | ||
48 | Matrix m2 = m1.clone(); | |
49 | assertNotSame(m1, m2); | |
50 | assertMatrixIsPristine(m2); | |
51 | } | |
52 | ||
53 | public void testMultiplication() throws Exception | |
54 | { | |
55 | // This matrix will not change - we use it to drive the various multiplications. | |
56 | final Matrix testMatrix = new Matrix(); | |
57 | ||
58 | // Create matrix with values | |
59 | // [ 0, 1, 2 | |
60 | // 1, 2, 3 | |
61 | // 2, 3, 4] | |
62 | for (int x = 0; x < 3; x++) | |
63 | { | |
64 | for (int y = 0; y < 3; y++) | |
65 | { | |
66 | testMatrix.setValue(x, y, x + y); | |
67 | } | |
68 | } | |
69 | ||
70 | Matrix m1 = testMatrix.clone(); | |
71 | Matrix m2 = testMatrix.clone(); | |
72 | ||
73 | // Multiply two matrices together producing a new result matrix. | |
74 | Matrix product = m1.multiply(m2); | |
75 | ||
76 | assertNotSame(m1, product); | |
77 | assertNotSame(m2, product); | |
78 | ||
79 | // Operand 1 should not have changed | |
80 | assertMatrixValuesEqualTo(new float[] {0, 1, 2, | |
81 | 1, 2, 3, | |
82 | 2, 3, 4}, m1); | |
83 | // Operand 2 should not have changed | |
84 | assertMatrixValuesEqualTo(new float[] {0, 1, 2, | |
85 | 1, 2, 3, | |
86 | 2, 3, 4}, m2); | |
87 | assertMatrixValuesEqualTo(new float[] {5, 8, 11, | |
88 | 8, 14, 20, | |
89 | 11, 20, 29}, product); | |
90 | ||
91 | // Multiply two matrices together with the result being written to a third matrix | |
92 | // (Any existing values there will be overwritten). | |
93 | Matrix resultMatrix = new Matrix(); | |
94 | ||
95 | Matrix retVal = m1.multiply(m2, resultMatrix); | |
96 | assertSame(retVal, resultMatrix); | |
97 | // Operand 1 should not have changed | |
98 | assertMatrixValuesEqualTo(new float[] {0, 1, 2, | |
99 | 1, 2, 3, | |
100 | 2, 3, 4}, m1); | |
101 | // Operand 2 should not have changed | |
102 | assertMatrixValuesEqualTo(new float[] {0, 1, 2, | |
103 | 1, 2, 3, | |
104 | 2, 3, 4}, m2); | |
105 | assertMatrixValuesEqualTo(new float[] {5, 8, 11, | |
106 | 8, 14, 20, | |
107 | 11, 20, 29}, resultMatrix); | |
108 | ||
109 | ||
110 | ||
111 | // Multiply two matrices together with the result being written into the other matrix | |
112 | retVal = m1.multiply(m2, m2); | |
113 | assertSame(retVal, m2); | |
114 | // Operand 1 should not have changed | |
115 | assertMatrixValuesEqualTo(new float[] {0, 1, 2, | |
116 | 1, 2, 3, | |
117 | 2, 3, 4}, m1); | |
118 | assertMatrixValuesEqualTo(new float[] {5, 8, 11, | |
119 | 8, 14, 20, | |
120 | 11, 20, 29}, retVal); | |
121 | ||
122 | ||
123 | ||
124 | // Multiply two matrices together with the result being written into 'this' matrix | |
125 | m1 = testMatrix.clone(); | |
126 | m2 = testMatrix.clone(); | |
127 | ||
128 | retVal = m1.multiply(m2, m1); | |
129 | assertSame(retVal, m1); | |
130 | // Operand 2 should not have changed | |
131 | assertMatrixValuesEqualTo(new float[] {0, 1, 2, | |
132 | 1, 2, 3, | |
133 | 2, 3, 4}, m2); | |
134 | assertMatrixValuesEqualTo(new float[] {5, 8, 11, | |
135 | 8, 14, 20, | |
136 | 11, 20, 29}, retVal); | |
137 | ||
138 | ||
139 | ||
140 | // Multiply the same matrix with itself with the result being written into 'this' matrix | |
141 | m1 = testMatrix.clone(); | |
142 | ||
143 | retVal = m1.multiply(m1, m1); | |
144 | assertSame(retVal, m1); | |
145 | assertMatrixValuesEqualTo(new float[] {5, 8, 11, | |
146 | 8, 14, 20, | |
147 | 11, 20, 29}, retVal); | |
148 | } | |
149 | ||
150 | /** | |
151 | * This method asserts that the matrix values for the given {@link Matrix} object are equal | |
152 | * to the pristine, or original, values. | |
153 | * @param m the Matrix to test. | |
154 | */ | |
155 | private void assertMatrixIsPristine(Matrix m) | |
156 | { | |
157 | assertMatrixValuesEqualTo(new float[] {1 ,0 ,0, | |
158 | 0, 1, 0, | |
159 | 0, 0, 1}, m); | |
160 | } | |
161 | ||
162 | /** | |
163 | * This method asserts that the matrix values for the given {@link Matrix} object have | |
164 | * the specified values. | |
165 | * @param values the expected values | |
166 | * @param m the matrix to test | |
167 | */ | |
168 | private void assertMatrixValuesEqualTo(float[] values, Matrix m) | |
169 | { | |
170 | float delta = 0.00001f; | |
171 | for (int i = 0; i < values.length; i++) | |
172 | { | |
173 | // Need to convert a (row, column) coordinate into a straight index. | |
174 | int row = (int)Math.floor(i / 3); | |
175 | int column = i % 3; | |
176 | StringBuilder failureMsg = new StringBuilder(); | |
177 | failureMsg.append("Incorrect value for matrix[") | |
178 | .append(row).append(",").append(column).append("]"); | |
179 | assertEquals(failureMsg.toString(), values[i], m.getValue(row, column), delta); | |
180 | } | |
181 | } | |
182 | ||
183 | /** | |
184 | * Set the tests in the suite for this test class. | |
185 | * | |
186 | * @return the Suite. | |
187 | */ | |
188 | public static Test suite() | |
189 | { | |
190 | return new TestSuite( TestMatrix.class ); | |
191 | } | |
192 | ||
193 | /** | |
194 | * Command line execution. | |
195 | * | |
196 | * @param args Command line arguments. | |
197 | */ | |
198 | public static void main( String[] args ) | |
199 | { | |
200 | String[] arg = {TestMatrix.class.getName() }; | |
201 | junit.textui.TestRunner.main( arg ); | |
202 | } | |
203 | } |
22 | 22 | <parent> |
23 | 23 | <groupId>org.apache.pdfbox</groupId> |
24 | 24 | <artifactId>pdfbox-parent</artifactId> |
25 | <version>2.0.18</version> | |
25 | <version>2.0.19</version> | |
26 | 26 | <relativePath>parent/pom.xml</relativePath> |
27 | 27 | </parent> |
28 | 28 | |
33 | 33 | |
34 | 34 | <scm> |
35 | 35 | <connection> |
36 | scm:svn:http://svn.apache.org/repos/asf/pdfbox/tags/2.0.18 | |
36 | scm:svn:http://svn.apache.org/repos/asf/pdfbox/tags/2.0.19 | |
37 | 37 | </connection> |
38 | 38 | <developerConnection> |
39 | scm:svn:https://svn.apache.org/repos/asf/pdfbox/tags/2.0.18 | |
39 | scm:svn:https://svn.apache.org/repos/asf/pdfbox/tags/2.0.19 | |
40 | 40 | </developerConnection> |
41 | <url>http://svn.apache.org/viewvc/pdfbox/tags/2.0.18</url> | |
41 | <url>http://svn.apache.org/viewvc/pdfbox/tags/2.0.19</url> | |
42 | 42 | </scm> |
43 | 43 | |
44 | 44 | <modules> |
25 | 25 | <parent> |
26 | 26 | <groupId>org.apache.pdfbox</groupId> |
27 | 27 | <artifactId>pdfbox-parent</artifactId> |
28 | <version>2.0.18</version> | |
28 | <version>2.0.19</version> | |
29 | 29 | <relativePath>../parent/pom.xml</relativePath> |
30 | 30 | </parent> |
31 | 31 |
+9
-8
27 | 27 | import org.apache.xmpbox.schema.XMPSchema; |
28 | 28 | |
29 | 29 | /** |
30 | * Class which all elements within an rdf:RDF have the same value for their rdf:about attributes | |
31 | * | |
30 | * Class which checks that all elements within an rdf:RDF have the same value for their rdf:about | |
31 | * attributes. | |
32 | * | |
32 | 33 | * @author Germain Costenobel |
33 | * | |
34 | * | |
34 | 35 | */ |
35 | 36 | public class RDFAboutAttributeConcordanceValidation |
36 | 37 | { |
37 | 38 | |
38 | 39 | /** |
39 | * | |
40 | * | |
40 | 41 | * @param metadata the XMP metadata. |
41 | 42 | * @throws DifferentRDFAboutException |
42 | 43 | * @throws ValidationException |
47 | 48 | List<XMPSchema> schemas = metadata.getAllSchemas(); |
48 | 49 | if (schemas.isEmpty()) |
49 | 50 | { |
50 | throw new ValidationException("Schemas not found in the given metadata representation"); | |
51 | throw new ValidationException("No schema found in the given metadata representation"); | |
51 | 52 | } |
52 | ||
53 | ||
53 | 54 | String about = schemas.get(0).getAboutValue(); |
54 | ||
55 | ||
55 | 56 | // rdf:description must have an rdf:about attribute |
56 | 57 | for (XMPSchema xmpSchema : schemas) |
57 | 58 | { |
61 | 62 | { |
62 | 63 | throw new DifferentRDFAboutException(); |
63 | 64 | } |
64 | ||
65 | ||
65 | 66 | if ("".equals(about)) |
66 | 67 | { |
67 | 68 | about = schemaAboutValue; |
22 | 22 | <parent> |
23 | 23 | <groupId>org.apache.pdfbox</groupId> |
24 | 24 | <artifactId>pdfbox-parent</artifactId> |
25 | <version>2.0.18</version> | |
25 | <version>2.0.19</version> | |
26 | 26 | <relativePath>../parent/pom.xml</relativePath> |
27 | 27 | </parent> |
28 | 28 |
22 | 22 | <parent> |
23 | 23 | <groupId>org.apache.pdfbox</groupId> |
24 | 24 | <artifactId>pdfbox-parent</artifactId> |
25 | <version>2.0.18</version> | |
25 | <version>2.0.19</version> | |
26 | 26 | <relativePath>../parent/pom.xml</relativePath> |
27 | 27 | </parent> |
28 | 28 |
15 | 15 | package org.apache.pdfbox.tools; |
16 | 16 | |
17 | 17 | import java.io.File; |
18 | import java.io.IOException; | |
18 | 19 | |
19 | 20 | import org.apache.pdfbox.cos.COSName; |
20 | 21 | import org.apache.pdfbox.cos.COSObject; |
94 | 95 | } |
95 | 96 | doc.save(outputFilename); |
96 | 97 | } |
97 | catch(Exception e) | |
98 | catch (IOException e) | |
98 | 99 | { |
99 | 100 | System.err.println("Error processing file: " + e.getMessage()); |
100 | 101 | } |
66 | 66 | COSName.DCT_DECODE.getName(), |
67 | 67 | COSName.DCT_DECODE_ABBREVIATION.getName()); |
68 | 68 | |
69 | private boolean directJPEG; | |
70 | private String prefix; | |
69 | private boolean useDirectJPEG; | |
70 | private String filePrefix; | |
71 | 71 | |
72 | 72 | private final Set<COSStream> seen = new HashSet<COSStream>(); |
73 | 73 | private int imageCounter = 1; |
120 | 120 | { |
121 | 121 | usage(); |
122 | 122 | } |
123 | prefix = args[i]; | |
123 | filePrefix = args[i]; | |
124 | 124 | } |
125 | 125 | else if (args[i].equals(DIRECTJPEG)) |
126 | 126 | { |
127 | directJPEG = true; | |
127 | useDirectJPEG = true; | |
128 | 128 | } |
129 | 129 | else |
130 | 130 | { |
140 | 140 | } |
141 | 141 | else |
142 | 142 | { |
143 | if (prefix == null && pdfFile.length() >4) | |
144 | { | |
145 | prefix = pdfFile.substring(0, pdfFile.length() -4); | |
143 | if (filePrefix == null && pdfFile.length() > 4) | |
144 | { | |
145 | filePrefix = pdfFile.substring(0, pdfFile.length() - 4); | |
146 | 146 | } |
147 | 147 | |
148 | 148 | extract(pdfFile, password); |
206 | 206 | PDPage page = getPage(); |
207 | 207 | processPage(page); |
208 | 208 | PDResources res = page.getResources(); |
209 | if (res == null) | |
210 | { | |
211 | return; | |
212 | } | |
209 | 213 | for (COSName name : res.getExtGStateNames()) |
210 | 214 | { |
211 | 215 | PDSoftMask softMask = res.getExtGState(name).getSoftMask(); |
242 | 246 | } |
243 | 247 | |
244 | 248 | // save image |
245 | String name = prefix + "-" + imageCounter; | |
249 | String name = filePrefix + "-" + imageCounter; | |
246 | 250 | imageCounter++; |
247 | 251 | |
248 | 252 | System.out.println("Writing image: " + name); |
249 | write2file(pdImage, name, directJPEG); | |
253 | write2file(pdImage, name, useDirectJPEG); | |
250 | 254 | } |
251 | 255 | |
252 | 256 | @Override |
354 | 358 | } |
355 | 359 | } |
356 | 360 | } |
357 | } | |
358 | ||
359 | private boolean hasMasks(PDImage pdImage) throws IOException | |
360 | { | |
361 | if (pdImage instanceof PDImageXObject) | |
362 | { | |
363 | PDImageXObject ximg = (PDImageXObject) pdImage; | |
364 | return ximg.getMask() != null || ximg.getSoftMask() != null; | |
365 | } | |
366 | return false; | |
367 | 361 | } |
368 | 362 | |
369 | 363 | /** |
387 | 381 | suffix = "jp2"; |
388 | 382 | } |
389 | 383 | |
384 | if (hasMasks(pdImage)) | |
385 | { | |
386 | // TIKA-3040, PDFBOX-4771: can't save ARGB as JPEG | |
387 | suffix = "png"; | |
388 | } | |
389 | ||
390 | 390 | FileOutputStream out = null; |
391 | 391 | try |
392 | 392 | { |
393 | 393 | out = new FileOutputStream(prefix + "." + suffix); |
394 | BufferedImage image = pdImage.getImage(); | |
395 | if (image != null) | |
396 | { | |
397 | if ("jpg".equals(suffix)) | |
398 | { | |
399 | String colorSpaceName = pdImage.getColorSpace().getName(); | |
400 | if (directJPEG || | |
401 | !hasMasks(pdImage) && | |
402 | (PDDeviceGray.INSTANCE.getName().equals(colorSpaceName) || | |
403 | PDDeviceRGB.INSTANCE.getName().equals(colorSpaceName))) | |
404 | { | |
405 | // RGB or Gray colorspace: get and write the unmodified JPEG stream | |
406 | InputStream data = pdImage.createInputStream(JPEG); | |
407 | IOUtils.copy(data, out); | |
408 | IOUtils.closeQuietly(data); | |
409 | } | |
410 | else | |
411 | { | |
412 | // for CMYK and other "unusual" colorspaces, the JPEG will be converted | |
394 | if ("jpg".equals(suffix)) | |
395 | { | |
396 | String colorSpaceName = pdImage.getColorSpace().getName(); | |
397 | if (directJPEG || | |
398 | (PDDeviceGray.INSTANCE.getName().equals(colorSpaceName) || | |
399 | PDDeviceRGB.INSTANCE.getName().equals(colorSpaceName))) | |
400 | { | |
401 | // RGB or Gray colorspace: get and write the unmodified JPEG stream | |
402 | InputStream data = pdImage.createInputStream(JPEG); | |
403 | IOUtils.copy(data, out); | |
404 | IOUtils.closeQuietly(data); | |
405 | } | |
406 | else | |
407 | { | |
408 | // for CMYK and other "unusual" colorspaces, the JPEG will be converted | |
409 | BufferedImage image = pdImage.getImage(); | |
410 | if (image != null) | |
411 | { | |
413 | 412 | ImageIOUtil.writeImage(image, suffix, out); |
414 | 413 | } |
415 | 414 | } |
416 | else if ("jp2".equals(suffix)) | |
417 | { | |
418 | String colorSpaceName = pdImage.getColorSpace().getName(); | |
419 | if (directJPEG || | |
420 | !hasMasks(pdImage) && | |
421 | (PDDeviceGray.INSTANCE.getName().equals(colorSpaceName) || | |
422 | PDDeviceRGB.INSTANCE.getName().equals(colorSpaceName))) | |
423 | { | |
424 | // RGB or Gray colorspace: get and write the unmodified JPEG2000 stream | |
425 | InputStream data = pdImage.createInputStream( | |
426 | Arrays.asList(COSName.JPX_DECODE.getName())); | |
427 | IOUtils.copy(data, out); | |
428 | IOUtils.closeQuietly(data); | |
429 | } | |
430 | else | |
431 | { | |
432 | // for CMYK and other "unusual" colorspaces, the image will be converted | |
415 | } | |
416 | else if ("jp2".equals(suffix)) | |
417 | { | |
418 | String colorSpaceName = pdImage.getColorSpace().getName(); | |
419 | if (directJPEG || | |
420 | (PDDeviceGray.INSTANCE.getName().equals(colorSpaceName) || | |
421 | PDDeviceRGB.INSTANCE.getName().equals(colorSpaceName))) | |
422 | { | |
423 | // RGB or Gray colorspace: get and write the unmodified JPEG2000 stream | |
424 | InputStream data = pdImage.createInputStream( | |
425 | Arrays.asList(COSName.JPX_DECODE.getName())); | |
426 | IOUtils.copy(data, out); | |
427 | IOUtils.closeQuietly(data); | |
428 | } | |
429 | else | |
430 | { | |
431 | // for CMYK and other "unusual" colorspaces, the image will be converted | |
432 | BufferedImage image = pdImage.getImage(); | |
433 | if (image != null) | |
434 | { | |
433 | 435 | ImageIOUtil.writeImage(image, "jpeg2000", out); |
434 | 436 | } |
435 | 437 | } |
436 | else | |
438 | } | |
439 | else if ("tiff".equals(suffix) && pdImage.getColorSpace().equals(PDDeviceGray.INSTANCE)) | |
440 | { | |
441 | BufferedImage image = pdImage.getImage(); | |
442 | if (image == null) | |
443 | { | |
444 | return; | |
445 | } | |
446 | // CCITT compressed images can have a different colorspace, but this one is B/W | |
447 | // This is a bitonal image, so copy to TYPE_BYTE_BINARY | |
448 | // so that a G4 compressed TIFF image is created by ImageIOUtil.writeImage() | |
449 | int w = image.getWidth(); | |
450 | int h = image.getHeight(); | |
451 | BufferedImage bitonalImage = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY); | |
452 | // copy image the old fashioned way - ColorConvertOp is slower! | |
453 | for (int y = 0; y < h; y++) | |
454 | { | |
455 | for (int x = 0; x < w; x++) | |
456 | { | |
457 | bitonalImage.setRGB(x, y, image.getRGB(x, y)); | |
458 | } | |
459 | } | |
460 | ImageIOUtil.writeImage(bitonalImage, suffix, out); | |
461 | } | |
462 | else | |
463 | { | |
464 | BufferedImage image = pdImage.getImage(); | |
465 | if (image != null) | |
437 | 466 | { |
438 | 467 | ImageIOUtil.writeImage(image, suffix, out); |
439 | 468 | } |
448 | 477 | } |
449 | 478 | } |
450 | 479 | } |
480 | ||
481 | private boolean hasMasks(PDImage pdImage) throws IOException | |
482 | { | |
483 | if (pdImage instanceof PDImageXObject) | |
484 | { | |
485 | PDImageXObject ximg = (PDImageXObject) pdImage; | |
486 | return ximg.getMask() != null || ximg.getSoftMask() != null; | |
487 | } | |
488 | return false; | |
489 | } | |
451 | 490 | } |
70 | 70 | /* |
71 | 71 | * debug flag |
72 | 72 | */ |
73 | private boolean debug = false; | |
73 | private boolean debugOutput = false; | |
74 | 74 | |
75 | 75 | /** |
76 | 76 | * private constructor. |
170 | 170 | } |
171 | 171 | else if( args[i].equals( DEBUG ) ) |
172 | 172 | { |
173 | debug = true; | |
173 | debugOutput = true; | |
174 | 174 | } |
175 | 175 | else if( args[i].equals( END_PAGE ) ) |
176 | 176 | { |
238 | 238 | output = new OutputStreamWriter( new FileOutputStream( outputFile ), encoding ); |
239 | 239 | } |
240 | 240 | startTime = startProcessing("Starting text extraction"); |
241 | if (debug) | |
241 | if (debugOutput) | |
242 | 242 | { |
243 | 243 | System.err.println("Writing to " + outputFile); |
244 | 244 | } |
287 | 287 | { |
288 | 288 | for (Map.Entry<String, PDComplexFileSpecification> ent : embeddedFileNames.entrySet()) |
289 | 289 | { |
290 | if (debug) | |
290 | if (debugOutput) | |
291 | 291 | { |
292 | 292 | System.err.println("Processing embedded file " + ent.getKey() + ":"); |
293 | 293 | } |
295 | 295 | PDEmbeddedFile file = spec.getEmbeddedFile(); |
296 | 296 | if (file != null && "application/pdf".equals(file.getSubtype())) |
297 | 297 | { |
298 | if (debug) | |
298 | if (debugOutput) | |
299 | 299 | { |
300 | 300 | System.err.println(" is PDF (size=" + file.getSize() + ")"); |
301 | 301 | } |
389 | 389 | |
390 | 390 | private long startProcessing(String message) |
391 | 391 | { |
392 | if (debug) | |
392 | if (debugOutput) | |
393 | 393 | { |
394 | 394 | System.err.println(message); |
395 | 395 | } |
398 | 398 | |
399 | 399 | private void stopProcessing(String message, long startTime) |
400 | 400 | { |
401 | if (debug) | |
401 | if (debugOutput) | |
402 | 402 | { |
403 | 403 | long stopTime = System.currentTimeMillis(); |
404 | 404 | float elapsedTime = ((float)(stopTime - startTime))/1000; |
269 | 269 | int count = 1 + endPage - startPage; |
270 | 270 | if (showTime) |
271 | 271 | { |
272 | System.err.printf("Rendered %d page%s in %dms\n", count, count == 1 ? "" : "s", | |
272 | System.err.printf("Rendered %d page%s in %dms%n", count, count == 1 ? "" : "s", | |
273 | 273 | duration / 1000000); |
274 | 274 | } |
275 | 275 |