| /* |
| * Copyright (c) 2014-2021 by Wen Yu |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the Eclipse |
| * Public License, v. 2.0 are satisfied: GNU General Public License, version 2 |
| * or any later version. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later |
| */ |
| |
| package pixy.image.png; |
| |
| import java.io.BufferedWriter; |
| import java.io.OutputStreamWriter; |
| import java.util.zip.DeflaterOutputStream; |
| import java.io.ByteArrayOutputStream; |
| |
| import pixy.util.Builder; |
| |
| /** |
| * Builder for PNG textual chunks: iTXT, zTXT, and tEXT. |
| * |
| * @author Wen Yu, yuwen_66@yahoo.com |
| * @version 1.0 12/31/2012 |
| */ |
| public class TextBuilder extends ChunkBuilder implements Builder<Chunk> { |
| // Whether or not iTXT should be compressed. iTXT is uncompressed by default |
| private boolean compressed; |
| private String keyword; |
| private String text; |
| |
| public TextBuilder(ChunkType chunkType) { |
| super(chunkType); |
| |
| if((chunkType != ChunkType.TEXT) && (chunkType != ChunkType.ITXT) |
| && (chunkType != ChunkType.ZTXT)) |
| throw new IllegalArgumentException("Expect Textual chunk!"); |
| } |
| |
| protected byte[] buildData() { |
| byte[] data = null; |
| ChunkType chunkType = getChunkType(); |
| |
| StringBuilder sb = new StringBuilder(this.keyword); |
| sb.append('\0'); |
| |
| switch (chunkType) { |
| case TEXT: |
| sb.append(this.text); |
| try { |
| data = sb.toString().getBytes("iso-8859-1"); |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| } |
| break; |
| case ZTXT: |
| try { |
| ByteArrayOutputStream bo = new ByteArrayOutputStream(1024); |
| sb.append('\0'); |
| bo.write(sb.toString().getBytes("iso-8859-1")); |
| DeflaterOutputStream ds = new DeflaterOutputStream(bo); |
| OutputStreamWriter or = new OutputStreamWriter(ds, "iso-8859-1"); |
| BufferedWriter br = new BufferedWriter(or); |
| br.write(this.text); |
| br.flush(); |
| br.close(); |
| data = bo.toByteArray(); |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| } |
| break; |
| case ITXT: |
| try { |
| ByteArrayOutputStream bo = new ByteArrayOutputStream(1024); |
| bo.write(sb.toString().getBytes("iso-8859-1")); |
| OutputStreamWriter or = null; |
| if(compressed) { |
| bo.write(new byte[]{1, 0, 0, 0}); |
| or = new OutputStreamWriter(new DeflaterOutputStream(bo), "UTF-8"); |
| } else { |
| bo.write(new byte[]{0, 0, 0, 0}); |
| or = new OutputStreamWriter(bo, "UTF-8"); |
| } |
| BufferedWriter br = new BufferedWriter(or); |
| br.write(this.text); |
| br.flush(); |
| br.close(); |
| data = bo.toByteArray(); |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| } |
| break; |
| default: // It will never come this far! |
| } |
| |
| return data; |
| } |
| |
| /** |
| * The keyword must be at least one character and less than 80 characters long. |
| * <p> |
| * Keywords are always interpreted according to the ISO/IEC 8859-1 (Latin-1) |
| * character set [ISO/IEC-8859-1]. |
| * <p> |
| * They must contain only printable Latin-1 characters and spaces; that is, |
| * only character codes 32-126 and 161-255 decimal are allowed. |
| * <p> |
| * To reduce the chances for human misreading of a keyword, leading and |
| * trailing spaces are forbidden, as are consecutive spaces. |
| * <p> |
| * Note also that the non-breaking space (code 160) is not permitted in keywords, |
| * since it is visually indistinguishable from an ordinary space. |
| */ |
| public TextBuilder keyword(String keyword) { |
| this.keyword = keyword.trim().replaceAll("\\s+", " "); |
| return this; |
| } |
| |
| public void setCompressed(boolean compressed) { |
| this.compressed = compressed; |
| } |
| |
| /** |
| * The tExt chunk is interpreted according to the ISO/IEC 8859-1 (Latin-1) character |
| * set [ISO/IEC-8859-1]. |
| * |
| * The text string can contain any Latin-1 character. Newlines in the text string |
| * should be represented by a single line feed character (decimal 10); use of other |
| * control characters in the text is discouraged. |
| * <p> |
| * The zTXt chunk contains textual data, just as tEXt does; however, zTXt takes |
| * advantage of compression. The zTXt and tEXt chunks are semantically equivalent, |
| * but zTXt is recommended for storing large blocks of text. |
| * A zTXt chunk contains: |
| * Keyword: 1-79 bytes (character string) |
| * Null separator: 1 byte |
| * Compression method: 1 byte |
| * Compressed text: n bytes |
| * <p> |
| * iTXt International textual data |
| * This chunk is semantically equivalent to the tEXt and zTXt chunks, but the textual |
| * data is in the UTF-8 encoding of the Unicode character set instead of Latin-1. |
| * This chunk contains: |
| * Keyword: 1-79 bytes (character string) |
| * Null separator: 1 byte |
| * Compression flag: 1 byte |
| * Compression method: 1 byte |
| * Language tag: 0 or more bytes (character string) |
| * Null separator: 1 byte |
| * Translated keyword: 0 or more bytes |
| * Null separator: 1 byte |
| * Text: 0 or more bytes |
| */ |
| public TextBuilder text(String text) { |
| this.text = text; |
| return this; |
| } |
| } |