Skip to content

Commit

Permalink
(#49) Refactoring in PngImage
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriciofx committed Dec 21, 2023
1 parent 9984c1c commit 2b14e48
Show file tree
Hide file tree
Showing 15 changed files with 891 additions and 117 deletions.
145 changes: 28 additions & 117 deletions src/main/java/com/github/fabriciofx/cactoos/pdf/content/PngImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@
package com.github.fabriciofx.cactoos.pdf.content;

import com.github.fabriciofx.cactoos.pdf.Count;
import com.github.fabriciofx.cactoos.pdf.Flow;
import com.github.fabriciofx.cactoos.pdf.Object;
import com.github.fabriciofx.cactoos.pdf.png.Body;
import com.github.fabriciofx.cactoos.pdf.png.Header;
import com.github.fabriciofx.cactoos.pdf.png.Img;
import com.github.fabriciofx.cactoos.pdf.png.Palette;
import com.github.fabriciofx.cactoos.pdf.png.PngImg;
import com.github.fabriciofx.cactoos.pdf.png.SafePngImg;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.file.Files;
import java.util.Arrays;
import org.cactoos.Bytes;
import org.cactoos.text.FormattedText;
import org.cactoos.text.Joined;
Expand All @@ -38,7 +42,7 @@
public final class PngImage implements Object, Bytes {
private final int number;
private final int generation;
private final int paletteReference;
private final Count count;
private final Bytes raw;

public PngImage(
Expand All @@ -48,20 +52,20 @@ public PngImage(
this(
count.increment(),
0,
count.increment(),
count,
() -> Files.readAllBytes(new File(filename).toPath())
);
}

public PngImage(
final int number,
final int generation,
final int paletteReference,
final Count count,
final Bytes raw
) {
this.number = number;
this.generation = generation;
this.paletteReference = paletteReference;
this.count = count;
this.raw = raw;
}

Expand All @@ -78,97 +82,11 @@ public String reference() {

@Override
public byte[] asBytes() throws Exception {
final Flow flow = new Flow(this.raw.asBytes());
final byte[] signature = {
(byte) 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'
};
if (!Arrays.equals(signature, flow.asBytes(8))) {
throw new Exception("Not a PNG image file");
}
flow.skip(4);
if (!flow.asString(4).equals("IHDR")) {
throw new Exception("Incorrect PNG image file");
}
final int width = flow.asInt();
final int height = flow.asInt();
final int bitsPerComponent = flow.asByte();
if (bitsPerComponent > 8) {
throw new Exception("16-bit depth in PNG file not supported");
}
final int colorType = flow.asByte();
final String colorSpace;
if (colorType == 0 || colorType == 4) {
colorSpace = "DeviceGray";
} else if (colorType == 2 || colorType == 6) {
colorSpace = "DeviceRGB";
} else if (colorType == 3) {
colorSpace = "Indexed";
} else {
throw new Exception("Unknown color type");
}
final int compressionMethod = flow.asByte();
if (compressionMethod != 0) {
throw new Exception("Unknown compression method");
}
final int filterMethod = flow.asByte();
if (filterMethod != 0) {
throw new Exception("Unknown filter method");
}
final int interlacing = flow.asByte();
if (interlacing != 0) {
throw new Exception("Interlacing not supported");
}
flow.skip(4);
final String dp = new FormattedText(
"/Predictor 15 /Colors %d /BitsPerComponent %d /Columns %d",
colorSpace.equals("DeviceRGB") ? 3 : 1,
bitsPerComponent,
width
).asString();
int n;
byte[] palette = new byte[0];
byte[] trns = new byte[0];
final ByteArrayOutputStream data = new ByteArrayOutputStream();
do {
n = flow.asInt();
final String type = flow.asString(4);
if (type.equals("PLTE")) {
palette = flow.asBytes(n);
flow.skip(4);
} else if (type.equals("tRNS")) {
final byte[] t = flow.asBytes(n);
if (colorType == 0) {
trns = new byte[]{(byte) new String(t).charAt(1)};
} else if (colorType == 2) {
trns = new byte[]{
(byte) new String(t).charAt(1),
(byte) new String(t).charAt(3),
(byte) new String(t).charAt(5)
};
} else {
final int pos = new String(t).indexOf('0');
if (pos != -1) {
trns = new byte[]{(byte) pos};
}
}
flow.skip(4);
} else if (type.equals("IDAT")) {
data.write(flow.asBytes(n));
flow.skip(4);
} else if (type.equals("IEND")) {
break;
} else {
flow.skip(n + 4);
}
} while (n > 0);
if (colorSpace.equals("Indexed") && palette.length == 0) {
throw new Exception("Missing palette in PNG file");
}
if (colorType >= 4) {
System.out.println("alpha channel");
}
final Img img = new SafePngImg(new PngImg(this.count, this.raw));
final Header header = img.header();
final Body body = img.body();
final Palette palette = img.palette();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Write the image (data) object
baos.write(
new FormattedText(
new Joined(
Expand All @@ -178,39 +96,32 @@ public byte[] asBytes() throws Exception {
"/Subtype /Image",
"/Width %d",
"/Height %d",
"/ColorSpace [/%s /DeviceRGB %d %d 0 R]",
"/ColorSpace [/%s /DeviceRGB %d %s]",
"/BitsPerComponent %d",
"/Filter /FlateDecode",
"/DecodeParms << %s >>",
"/DecodeParms << /Predictor 15 /Colors %d /BitsPerComponent %d /Columns %d >>",
"/Mask [0 0]",
"/Length %d >>",
"stream\n"
),
this.number,
this.generation,
width,
height,
colorSpace,
palette.length / 3 - 1,
this.paletteReference,
bitsPerComponent,
dp,
data.toByteArray().length
header.width(),
header.height(),
header.color().space(),
palette.stream().length / 3 - 1,
palette.reference(),
header.depth(),
header.color().space().equals("DeviceRGB") ? 3 : 1,
header.depth(),
header.width(),
body.stream().length
).asString().getBytes()
);
baos.write(data.toByteArray());
baos.write(body.stream());
baos.write("\nendstream\nendobj\n".getBytes());
// Write the palette object
baos.write(
new FormattedText(
"%d %d obj\n<< /Length %d >>\nstream\n",
this.paletteReference,
0,
palette.length
).asString().getBytes()
);
baos.write(palette);
baos.write("\nendstream\nendobj\n".getBytes());
baos.write(palette.asBytes());
return baos.toByteArray();
}
}
6 changes: 6 additions & 0 deletions src/main/java/com/github/fabriciofx/cactoos/pdf/png/Body.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.github.fabriciofx.cactoos.pdf.png;

import com.github.fabriciofx.cactoos.pdf.Content;

public interface Body extends Content {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.github.fabriciofx.cactoos.pdf.png;

import org.cactoos.Bytes;
import org.cactoos.Scalar;

public final class BytesAsInteger implements Scalar<Integer> {
private final Bytes bytes;

public BytesAsInteger(final byte[] bytes) {
this(() -> bytes);
}

public BytesAsInteger(final Bytes bytes) {
this.bytes = bytes;
}

@Override
public Integer value() throws Exception {
final byte[] raw = this.bytes.asBytes();
return ((raw[0] & 0xFF) << 24) |
((raw[1] & 0xFF) << 16) |
((raw[2] & 0xFF) << 8) |
(raw[3] & 0xFF);
}
}
56 changes: 56 additions & 0 deletions src/main/java/com/github/fabriciofx/cactoos/pdf/png/Color.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* The MIT License (MIT)
*
* Copyright (C) 2023 Fabrício Barros Cabral
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.fabriciofx.cactoos.pdf.png;

public final class Color {
private int type;

public Color(final int type) {
this.type = type;
}

public int type() {
return this.type;
}

public String space() {
final String space;
switch(this.type) {
case 0:
case 4:
space = "DeviceGray";
break;
case 2:
case 6:
space = "DeviceRGB";
break;
case 3:
space = "Indexed";
break;
default:
space = "Unknown";
}
return space;
}
}
64 changes: 64 additions & 0 deletions src/main/java/com/github/fabriciofx/cactoos/pdf/png/Header.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* The MIT License (MIT)
*
* Copyright (C) 2023 Fabrício Barros Cabral
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.fabriciofx.cactoos.pdf.png;

import org.cactoos.Bytes;
import org.cactoos.Text;

/**
* PNG Image Header
*
* PNG Image Header Structure:
* ----------------------------
*
* Offset Length Field Name Description
* ---------------------------------------------
* 0 8 bytes Signature PNG file signature (137 80 78 71 13 10 26 10)
* 8 4 bytes Chunk Length Length of the chunk data (excluding length and type)
* 12 4 bytes Chunk Type Type of the chunk (e.g., IHDR for image header)
* 16 4 bytes Width Width of the image in pixels
* 20 4 bytes Height Height of the image in pixels
* 24 1 byte Bit Depth Number of bits per sample
* 25 1 byte Color Type Type of color encoding used
* 26 1 byte Compression Compression method used
* 27 1 byte Filter Method Filtering method used
* 28 1 byte Interlace Method Interlace method used
*
* Additional chunks (not included in the header):
* - Data chunks (IDAT) containing the image data
* - Palette chunks (PLTE) for indexed-color images
* - Transparency chunks (tRNS) for specifying transparency
* - Textual information chunks (tEXt, iTXt) for metadata
* - Other optional chunks for various purposes
*/
public interface Header extends Text, Bytes {
int length();
int width();
int height();
int depth();
Color color();
int compression();
int filter();
int interlacing();
}
30 changes: 30 additions & 0 deletions src/main/java/com/github/fabriciofx/cactoos/pdf/png/Img.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* The MIT License (MIT)
*
* Copyright (C) 2023 Fabrício Barros Cabral
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.fabriciofx.cactoos.pdf.png;

public interface Img {
Header header() throws Exception;
Body body() throws Exception;
Palette palette() throws Exception;
}
Loading

0 comments on commit 2b14e48

Please sign in to comment.