1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.formats.pnm;
18
19 import static org.apache.commons.imaging.common.BinaryFunctions.readByte;
20
21 import java.awt.Dimension;
22 import java.awt.image.BufferedImage;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.io.PrintWriter;
27 import java.nio.ByteOrder;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.StringTokenizer;
31
32 import org.apache.commons.imaging.ImageFormat;
33 import org.apache.commons.imaging.ImageFormats;
34 import org.apache.commons.imaging.ImageInfo;
35 import org.apache.commons.imaging.ImageParser;
36 import org.apache.commons.imaging.ImageReadException;
37 import org.apache.commons.imaging.ImageWriteException;
38 import org.apache.commons.imaging.common.ImageBuilder;
39 import org.apache.commons.imaging.common.ImageMetadata;
40 import org.apache.commons.imaging.common.bytesource.ByteSource;
41 import org.apache.commons.imaging.palette.PaletteFactory;
42
43 public class PnmImageParser extends ImageParser<PnmImagingParameters> {
44 private static final String DEFAULT_EXTENSION = ImageFormats.PNM.getDefaultExtension();
45 private static final String[] ACCEPTED_EXTENSIONS = {
46 ImageFormats.PAM.getDefaultExtension(),
47 ImageFormats.PBM.getDefaultExtension(),
48 ImageFormats.PGM.getDefaultExtension(),
49 ImageFormats.PNM.getDefaultExtension(),
50 ImageFormats.PPM.getDefaultExtension()
51 };
52
53 public PnmImageParser() {
54 super.setByteOrder(ByteOrder.LITTLE_ENDIAN);
55 }
56
57 @Override
58 public PnmImagingParameters getDefaultParameters() {
59 return new PnmImagingParameters();
60 }
61
62 @Override
63 public String getName() {
64 return "Pbm-Custom";
65 }
66
67 @Override
68 public String getDefaultExtension() {
69 return DEFAULT_EXTENSION;
70 }
71
72 @Override
73 protected String[] getAcceptedExtensions() {
74 return ACCEPTED_EXTENSIONS;
75 }
76
77 @Override
78 protected ImageFormat[] getAcceptedTypes() {
79 return new ImageFormat[] {
80 ImageFormats.PBM,
81 ImageFormats.PGM,
82 ImageFormats.PPM,
83 ImageFormats.PNM,
84 ImageFormats.PAM
85 };
86 }
87
88 private FileInfo readHeader(final InputStream is) throws ImageReadException,
89 IOException {
90 final byte identifier1 = readByte("Identifier1", is, "Not a Valid PNM File");
91 final byte identifier2 = readByte("Identifier2", is, "Not a Valid PNM File");
92
93 if (identifier1 != PnmConstants.PNM_PREFIX_BYTE) {
94 throw new ImageReadException("PNM file has invalid prefix byte 1");
95 }
96
97 final WhiteSpaceReaderrmats/pnm/WhiteSpaceReader.html#WhiteSpaceReader">WhiteSpaceReader wsr = new WhiteSpaceReader(is);
98
99 if (identifier2 == PnmConstants.PBM_TEXT_CODE
100 || identifier2 == PnmConstants.PBM_RAW_CODE
101 || identifier2 == PnmConstants.PGM_TEXT_CODE
102 || identifier2 == PnmConstants.PGM_RAW_CODE
103 || identifier2 == PnmConstants.PPM_TEXT_CODE
104 || identifier2 == PnmConstants.PPM_RAW_CODE) {
105
106 final int width;
107 try {
108 width = Integer.parseInt(wsr.readtoWhiteSpace());
109 } catch (final NumberFormatException e) {
110 throw new ImageReadException("Invalid width specified." , e);
111 }
112 final int height;
113 try {
114 height = Integer.parseInt(wsr.readtoWhiteSpace());
115 } catch (final NumberFormatException e) {
116 throw new ImageReadException("Invalid height specified." , e);
117 }
118
119 if (identifier2 == PnmConstants.PBM_TEXT_CODE) {
120 return new PbmFileInfo(width, height, false);
121 }
122 if (identifier2 == PnmConstants.PBM_RAW_CODE) {
123 return new PbmFileInfo(width, height, true);
124 }
125 if (identifier2 == PnmConstants.PGM_TEXT_CODE) {
126 final int maxgray = Integer.parseInt(wsr.readtoWhiteSpace());
127 return new PgmFileInfo(width, height, false, maxgray);
128 }
129 if (identifier2 == PnmConstants.PGM_RAW_CODE) {
130 final int maxgray = Integer.parseInt(wsr.readtoWhiteSpace());
131 return new PgmFileInfo(width, height, true, maxgray);
132 }
133 if (identifier2 == PnmConstants.PPM_TEXT_CODE) {
134 final int max = Integer.parseInt(wsr.readtoWhiteSpace());
135 return new PpmFileInfo(width, height, false, max);
136 }
137 if (identifier2 == PnmConstants.PPM_RAW_CODE) {
138 final int max = Integer.parseInt(wsr.readtoWhiteSpace());
139 return new PpmFileInfo(width, height, true, max);
140 }
141 } else if (identifier2 == PnmConstants.PAM_RAW_CODE) {
142 int width = -1;
143 boolean seenWidth = false;
144 int height = -1;
145 boolean seenHeight = false;
146 int depth = -1;
147 boolean seenDepth = false;
148 int maxVal = -1;
149 boolean seenMaxVal = false;
150 final StringBuilder tupleType = new StringBuilder();
151 boolean seenTupleType = false;
152
153
154 wsr.readLine();
155 String line;
156 while ((line = wsr.readLine()) != null) {
157 line = line.trim();
158 if (line.charAt(0) == '#') {
159 continue;
160 }
161 final StringTokenizer tokenizer = new StringTokenizer(line, " ", false);
162 final String type = tokenizer.nextToken();
163 if ("WIDTH".equals(type)) {
164 seenWidth = true;
165 if(!tokenizer.hasMoreTokens()) {
166 throw new ImageReadException("PAM header has no WIDTH value");
167 }
168 width = Integer.parseInt(tokenizer.nextToken());
169 } else if ("HEIGHT".equals(type)) {
170 seenHeight = true;
171 if(!tokenizer.hasMoreTokens()) {
172 throw new ImageReadException("PAM header has no HEIGHT value");
173 }
174 height = Integer.parseInt(tokenizer.nextToken());
175 } else if ("DEPTH".equals(type)) {
176 seenDepth = true;
177 if(!tokenizer.hasMoreTokens()) {
178 throw new ImageReadException("PAM header has no DEPTH value");
179 }
180 depth = Integer.parseInt(tokenizer.nextToken());
181 } else if ("MAXVAL".equals(type)) {
182 seenMaxVal = true;
183 if(!tokenizer.hasMoreTokens()) {
184 throw new ImageReadException("PAM header has no MAXVAL value");
185 }
186 maxVal = Integer.parseInt(tokenizer.nextToken());
187 } else if ("TUPLTYPE".equals(type)) {
188 seenTupleType = true;
189 if(!tokenizer.hasMoreTokens()) {
190 throw new ImageReadException("PAM header has no TUPLTYPE value");
191 }
192 tupleType.append(tokenizer.nextToken());
193 } else if ("ENDHDR".equals(type)) {
194 break;
195 } else {
196 throw new ImageReadException("Invalid PAM file header type " + type);
197 }
198 }
199
200 if (!seenWidth) {
201 throw new ImageReadException("PAM header has no WIDTH");
202 }
203 if (!seenHeight) {
204 throw new ImageReadException("PAM header has no HEIGHT");
205 }
206 if (!seenDepth) {
207 throw new ImageReadException("PAM header has no DEPTH");
208 }
209 if (!seenMaxVal) {
210 throw new ImageReadException("PAM header has no MAXVAL");
211 }
212 if (!seenTupleType) {
213 throw new ImageReadException("PAM header has no TUPLTYPE");
214 }
215
216 return new PamFileInfo(width, height, depth, maxVal, tupleType.toString());
217 }
218 throw new ImageReadException("PNM file has invalid prefix byte 2");
219 }
220
221 private FileInfo readHeader(final ByteSource byteSource)
222 throws ImageReadException, IOException {
223 try (InputStream is = byteSource.getInputStream()) {
224 return readHeader(is);
225 }
226 }
227
228 @Override
229 public byte[] getICCProfileBytes(final ByteSource byteSource, final PnmImagingParameters params)
230 throws ImageReadException, IOException {
231 return null;
232 }
233
234 @Override
235 public Dimension getImageSize(final ByteSource byteSource, final PnmImagingParameters params)
236 throws ImageReadException, IOException {
237 final FileInfo info = readHeader(byteSource);
238
239 return new Dimension(info.width, info.height);
240 }
241
242 @Override
243 public ImageMetadata getMetadata(final ByteSource byteSource, final PnmImagingParameters params)
244 throws ImageReadException, IOException {
245 return null;
246 }
247
248 @Override
249 public ImageInfo getImageInfo(final ByteSource byteSource, final PnmImagingParameters params)
250 throws ImageReadException, IOException {
251 final FileInfo info = readHeader(byteSource);
252
253 final List<String> comments = new ArrayList<>();
254
255 final int bitsPerPixel = info.getBitDepth() * info.getNumComponents();
256 final ImageFormat format = info.getImageType();
257 final String formatName = info.getImageTypeDescription();
258 final String mimeType = info.getMIMEType();
259 final int numberOfImages = 1;
260 final boolean progressive = false;
261
262
263
264 final int physicalWidthDpi = 72;
265 final float physicalWidthInch = (float) ((double) info.width / (double) physicalWidthDpi);
266 final int physicalHeightDpi = 72;
267 final float physicalHeightInch = (float) ((double) info.height / (double) physicalHeightDpi);
268
269 final String formatDetails = info.getImageTypeDescription();
270
271 final boolean transparent = info.hasAlpha();
272 final boolean usesPalette = false;
273
274 final ImageInfo.ColorType colorType = info.getColorType();
275 final ImageInfo.CompressionAlgorithm compressionAlgorithm = ImageInfo.CompressionAlgorithm.NONE;
276
277 return new ImageInfo(formatDetails, bitsPerPixel, comments,
278 format, formatName, info.height, mimeType, numberOfImages,
279 physicalHeightDpi, physicalHeightInch, physicalWidthDpi,
280 physicalWidthInch, info.width, progressive, transparent,
281 usesPalette, colorType, compressionAlgorithm);
282 }
283
284 @Override
285 public boolean dumpImageFile(final PrintWriter pw, final ByteSource byteSource)
286 throws ImageReadException, IOException {
287 pw.println("pnm.dumpImageFile");
288
289 final ImageInfo imageData = getImageInfo(byteSource);
290 if (imageData == null) {
291 return false;
292 }
293
294 imageData.toString(pw, "");
295
296 pw.println("");
297
298 return true;
299 }
300
301 @Override
302 public BufferedImage getBufferedImage(final ByteSource byteSource, final PnmImagingParameters params)
303 throws ImageReadException, IOException {
304 try (InputStream is = byteSource.getInputStream()) {
305 final FileInfo info = readHeader(is);
306
307 final int width = info.width;
308 final int height = info.height;
309
310 final boolean hasAlpha = info.hasAlpha();
311 final ImageBuilderImageBuilder.html#ImageBuilder">ImageBuilder imageBuilder = new ImageBuilder(width, height,
312 hasAlpha);
313 info.readImage(imageBuilder, is);
314
315 return imageBuilder.getBufferedImage();
316 }
317 }
318
319 @Override
320 public void writeImage(final BufferedImage src, final OutputStream os, PnmImagingParameters params)
321 throws ImageWriteException, IOException {
322 PnmWriter writer = null;
323 boolean useRawbits = true;
324
325 if (params != null) {
326 useRawbits = params.isRawBits();
327
328 final ImageFormats subtype = params.getSubtype();
329 if (subtype != null) {
330 if (subtype.equals(ImageFormats.PBM)) {
331 writer = new PbmWriter(useRawbits);
332 } else if (subtype.equals(ImageFormats.PGM)) {
333 writer = new PgmWriter(useRawbits);
334 } else if (subtype.equals(ImageFormats.PPM)) {
335 writer = new PpmWriter(useRawbits);
336 } else if (subtype.equals(ImageFormats.PAM)) {
337 writer = new PamWriter();
338 }
339 }
340 }
341
342 if (writer == null) {
343 final boolean hasAlpha = new PaletteFactory().hasTransparency(src);
344 if (hasAlpha) {
345 writer = new PamWriter();
346 } else {
347 writer = new PpmWriter(useRawbits);
348 }
349 }
350
351 writer.writeImage(src, os, params);
352 }
353 }