001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.imaging.formats.dcx; 018 019import static org.apache.commons.imaging.common.BinaryFunctions.read4Bytes; 020 021import java.awt.Dimension; 022import java.awt.image.BufferedImage; 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.OutputStream; 026import java.io.PrintWriter; 027import java.nio.ByteOrder; 028import java.util.ArrayList; 029import java.util.List; 030 031import org.apache.commons.imaging.ImageFormat; 032import org.apache.commons.imaging.ImageFormats; 033import org.apache.commons.imaging.ImageInfo; 034import org.apache.commons.imaging.ImageParser; 035import org.apache.commons.imaging.ImageReadException; 036import org.apache.commons.imaging.ImageWriteException; 037import org.apache.commons.imaging.common.BinaryOutputStream; 038import org.apache.commons.imaging.common.ImageMetadata; 039import org.apache.commons.imaging.common.bytesource.ByteSource; 040import org.apache.commons.imaging.common.bytesource.ByteSourceInputStream; 041import org.apache.commons.imaging.formats.pcx.PcxImageParser; 042import org.apache.commons.imaging.formats.pcx.PcxImagingParameters; 043 044public class DcxImageParser extends ImageParser<PcxImagingParameters> { 045 // See http://www.fileformat.fine/format/pcx/egff.htm for documentation 046 private static final String DEFAULT_EXTENSION = ImageFormats.DCX.getDefaultExtension(); 047 private static final String[] ACCEPTED_EXTENSIONS = ImageFormats.DCX.getExtensions(); 048 049 public DcxImageParser() { 050 super.setByteOrder(ByteOrder.LITTLE_ENDIAN); 051 } 052 053 @Override 054 public PcxImagingParameters getDefaultParameters() { 055 return new PcxImagingParameters(); 056 } 057 058 @Override 059 public String getName() { 060 return "Dcx-Custom"; 061 } 062 063 @Override 064 public String getDefaultExtension() { 065 return DEFAULT_EXTENSION; 066 } 067 068 @Override 069 protected String[] getAcceptedExtensions() { 070 return ACCEPTED_EXTENSIONS; 071 } 072 073 @Override 074 protected ImageFormat[] getAcceptedTypes() { 075 return new ImageFormat[] { ImageFormats.DCX }; 076 } 077 078 // FIXME should throw UOE 079 @Override 080 public ImageMetadata getMetadata(final ByteSource byteSource, final PcxImagingParameters params) 081 throws ImageReadException, IOException { 082 return null; 083 } 084 085 // FIXME should throw UOE 086 @Override 087 public ImageInfo getImageInfo(final ByteSource byteSource, final PcxImagingParameters params) 088 throws ImageReadException, IOException { 089 return null; 090 } 091 092 // FIXME should throw UOE 093 @Override 094 public Dimension getImageSize(final ByteSource byteSource, final PcxImagingParameters params) 095 throws ImageReadException, IOException { 096 return null; 097 } 098 099 // FIXME should throw UOE 100 @Override 101 public byte[] getICCProfileBytes(final ByteSource byteSource, final PcxImagingParameters params) 102 throws ImageReadException, IOException { 103 return null; 104 } 105 106 private static class DcxHeader { 107 108 public static final int DCX_ID = 0x3ADE68B1; 109 public final int id; 110 public final long[] pageTable; 111 112 DcxHeader(final int id, final long[] pageTable) { 113 this.id = id; 114 this.pageTable = pageTable; 115 } 116 117 public void dump(final PrintWriter pw) { 118 pw.println("DcxHeader"); 119 pw.println("Id: 0x" + Integer.toHexString(id)); 120 pw.println("Pages: " + pageTable.length); 121 pw.println(); 122 } 123 } 124 125 private DcxHeader readDcxHeader(final ByteSource byteSource) 126 throws ImageReadException, IOException { 127 try (InputStream is = byteSource.getInputStream()) { 128 final int id = read4Bytes("Id", is, "Not a Valid DCX File", getByteOrder()); 129 final List<Long> pageTable = new ArrayList<>(1024); 130 for (int i = 0; i < 1024; i++) { 131 final long pageOffset = 0xFFFFffffL & read4Bytes("PageTable", is, 132 "Not a Valid DCX File", getByteOrder()); 133 if (pageOffset == 0) { 134 break; 135 } 136 pageTable.add(pageOffset); 137 } 138 139 if (id != DcxHeader.DCX_ID) { 140 throw new ImageReadException( 141 "Not a Valid DCX File: file id incorrect"); 142 } 143 if (pageTable.size() == 1024) { 144 throw new ImageReadException( 145 "DCX page table not terminated by zero entry"); 146 } 147 148 final Object[] objects = pageTable.toArray(); 149 final long[] pages = new long[objects.length]; 150 for (int i = 0; i < objects.length; i++) { 151 pages[i] = ((Long) objects[i]); 152 } 153 154 return new DcxHeader(id, pages); 155 } 156 } 157 158 @Override 159 public boolean dumpImageFile(final PrintWriter pw, final ByteSource byteSource) 160 throws ImageReadException, IOException { 161 readDcxHeader(byteSource).dump(pw); 162 return true; 163 } 164 165 @Override 166 public final BufferedImage getBufferedImage(final ByteSource byteSource, 167 final PcxImagingParameters params) throws ImageReadException, IOException { 168 final List<BufferedImage> list = getAllBufferedImages(byteSource); 169 if (list.isEmpty()) { 170 return null; 171 } 172 return list.get(0); 173 } 174 175 @Override 176 public List<BufferedImage> getAllBufferedImages(final ByteSource byteSource) 177 throws ImageReadException, IOException { 178 final DcxHeader dcxHeader = readDcxHeader(byteSource); 179 final List<BufferedImage> images = new ArrayList<>(); 180 final PcxImageParser pcxImageParser = new PcxImageParser(); 181 for (final long element : dcxHeader.pageTable) { 182 try (InputStream stream = byteSource.getInputStream(element)) { 183 final ByteSourceInputStream pcxSource = new ByteSourceInputStream( 184 stream, null); 185 final BufferedImage image = pcxImageParser.getBufferedImage( 186 pcxSource, new PcxImagingParameters()); 187 images.add(image); 188 } 189 } 190 return images; 191 } 192 193 @Override 194 public void writeImage(final BufferedImage src, final OutputStream os, final PcxImagingParameters params) 195 throws ImageWriteException, IOException { 196 final int headerSize = 4 + 1024 * 4; 197 198 final BinaryOutputStream bos = new BinaryOutputStream(os, 199 ByteOrder.LITTLE_ENDIAN); 200 bos.write4Bytes(DcxHeader.DCX_ID); 201 // Some apps may need a full 1024 entry table 202 bos.write4Bytes(headerSize); 203 for (int i = 0; i < 1023; i++) { 204 bos.write4Bytes(0); 205 } 206 final PcxImageParser pcxImageParser = new PcxImageParser(); 207 pcxImageParser.writeImage(src, bos, params); 208 } 209}