1 package jogamp.opengl.util.pngj.chunks;
2 
3 import java.util.ArrayList;
4 import java.util.List;
5 
6 import jogamp.opengl.util.pngj.PngHelperInternal;
7 import jogamp.opengl.util.pngj.PngjException;
8 
9 /**
10  * We consider "image metadata" every info inside the image except for the most
11  * basic image info (IHDR chunk - ImageInfo class) and the pixels values.
12  * <p>
13  * This includes the palette (if present) and all the ancillary chunks
14  * <p>
15  * This class provides a wrapper over the collection of chunks of a image (read
16  * or to write) and provides some high level methods to access them
17  */
18 public class PngMetadata {
19 	private final ChunksList chunkList;
20 	private final boolean readonly;
21 
PngMetadata(final ChunksList chunks)22 	public PngMetadata(final ChunksList chunks) {
23 		this.chunkList = chunks;
24 		if (chunks instanceof ChunksListForWrite) {
25 			this.readonly = false;
26 		} else {
27 			this.readonly = true;
28 		}
29 	}
30 
31 	/**
32 	 * Queues the chunk at the writer
33 	 * <p>
34 	 * lazyOverwrite: if true, checks if there is a queued "equivalent" chunk
35 	 * and if so, overwrites it. However if that not check for already written
36 	 * chunks.
37 	 */
queueChunk(final PngChunk c, final boolean lazyOverwrite)38 	public void queueChunk(final PngChunk c, final boolean lazyOverwrite) {
39 		final ChunksListForWrite cl = getChunkListW();
40 		if (readonly)
41 			throw new PngjException("cannot set chunk : readonly metadata");
42 		if (lazyOverwrite) {
43 			ChunkHelper.trimList(cl.getQueuedChunks(), new ChunkPredicate() {
44 				@Override
45 				public boolean match(final PngChunk c2) {
46 					return ChunkHelper.equivalent(c, c2);
47 				}
48 			});
49 		}
50 		cl.queue(c);
51 	}
52 
queueChunk(final PngChunk c)53 	public void queueChunk(final PngChunk c) {
54 		queueChunk(c, true);
55 	}
56 
getChunkListW()57 	private ChunksListForWrite getChunkListW() {
58 		return (ChunksListForWrite) chunkList;
59 	}
60 
61 	// ///// high level utility methods follow ////////////
62 
63 	// //////////// DPI
64 
65 	/**
66 	 * returns -1 if not found or dimension unknown
67 	 */
getDpi()68 	public double[] getDpi() {
69 		final PngChunk c = chunkList.getById1(ChunkHelper.pHYs, true);
70 		if (c == null)
71 			return new double[] { -1, -1 };
72 		else
73 			return ((PngChunkPHYS) c).getAsDpi2();
74 	}
75 
setDpi(final double x)76 	public void setDpi(final double x) {
77 		setDpi(x, x);
78 	}
79 
setDpi(final double x, final double y)80 	public void setDpi(final double x, final double y) {
81 		final PngChunkPHYS c = new PngChunkPHYS(chunkList.imageInfo);
82 		c.setAsDpi2(x, y);
83 		queueChunk(c);
84 	}
85 
86 	// //////////// TIME
87 
88 	/**
89 	 * Creates a time chunk with current time, less secsAgo seconds
90 	 * <p>
91 	 *
92 	 * @return Returns the created-queued chunk, just in case you want to
93 	 *         examine or modify it
94 	 */
setTimeNow(final int secsAgo)95 	public PngChunkTIME setTimeNow(final int secsAgo) {
96 		final PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo);
97 		c.setNow(secsAgo);
98 		queueChunk(c);
99 		return c;
100 	}
101 
setTimeNow()102 	public PngChunkTIME setTimeNow() {
103 		return setTimeNow(0);
104 	}
105 
106 	/**
107 	 * Creates a time chunk with diven date-time
108 	 * <p>
109 	 *
110 	 * @return Returns the created-queued chunk, just in case you want to
111 	 *         examine or modify it
112 	 */
setTimeYMDHMS(final int yearx, final int monx, final int dayx, final int hourx, final int minx, final int secx)113 	public PngChunkTIME setTimeYMDHMS(final int yearx, final int monx, final int dayx, final int hourx, final int minx, final int secx) {
114 		final PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo);
115 		c.setYMDHMS(yearx, monx, dayx, hourx, minx, secx);
116 		queueChunk(c, true);
117 		return c;
118 	}
119 
120 	/**
121 	 * null if not found
122 	 */
getTime()123 	public PngChunkTIME getTime() {
124 		return (PngChunkTIME) chunkList.getById1(ChunkHelper.tIME);
125 	}
126 
getTimeAsString()127 	public String getTimeAsString() {
128 		final PngChunkTIME c = getTime();
129 		return c == null ? "" : c.getAsString();
130 	}
131 
132 	// //////////// TEXT
133 
134 	/**
135 	 * Creates a text chunk and queue it.
136 	 * <p>
137 	 *
138 	 * @param k
139 	 *            : key (latin1)
140 	 * @param val
141 	 *            (arbitrary, should be latin1 if useLatin1)
142 	 * @param useLatin1
143 	 * @param compress
144 	 * @return Returns the created-queued chunks, just in case you want to
145 	 *         examine, touch it
146 	 */
setText(final String k, final String val, final boolean useLatin1, final boolean compress)147 	public PngChunkTextVar setText(final String k, final String val, final boolean useLatin1, final boolean compress) {
148 		if (compress && !useLatin1)
149 			throw new PngjException("cannot compress non latin text");
150 		PngChunkTextVar c;
151 		if (useLatin1) {
152 			if (compress) {
153 				c = new PngChunkZTXT(chunkList.imageInfo);
154 			} else {
155 				c = new PngChunkTEXT(chunkList.imageInfo);
156 			}
157 		} else {
158 			c = new PngChunkITXT(chunkList.imageInfo);
159 			((PngChunkITXT) c).setLangtag(k); // we use the same orig tag (this is not quite right)
160 		}
161 		c.setKeyVal(k, val);
162 		queueChunk(c, true);
163 		return c;
164 	}
165 
setText(final String k, final String val)166 	public PngChunkTextVar setText(final String k, final String val) {
167 		return setText(k, val, false, false);
168 	}
169 
170 	/**
171 	 * gets all text chunks with a given key
172 	 * <p>
173 	 * returns null if not found
174 	 * <p>
175 	 * Warning: this does not check the "lang" key of iTxt
176 	 */
177 	@SuppressWarnings("unchecked")
getTxtsForKey(final String k)178 	public List<? extends PngChunkTextVar> getTxtsForKey(final String k) {
179 		@SuppressWarnings("rawtypes")
180         final
181 		List c = new ArrayList();
182 		c.addAll(chunkList.getById(ChunkHelper.tEXt, k));
183 		c.addAll(chunkList.getById(ChunkHelper.zTXt, k));
184 		c.addAll(chunkList.getById(ChunkHelper.iTXt, k));
185 		return c;
186 	}
187 
188 	/**
189 	 * Returns empty if not found, concatenated (with newlines) if multiple! -
190 	 * and trimmed
191 	 * <p>
192 	 * Use getTxtsForKey() if you don't want this behaviour
193 	 */
getTxtForKey(final String k)194 	public String getTxtForKey(final String k) {
195 		final List<? extends PngChunkTextVar> li = getTxtsForKey(k);
196 		if (li.isEmpty())
197 			return "";
198 		final StringBuilder t = new StringBuilder();
199 		for (final PngChunkTextVar c : li)
200 			t.append(c.getVal()).append("\n");
201 		return t.toString().trim();
202 	}
203 
204 	/**
205 	 * Returns the palette chunk, if present
206 	 *
207 	 * @return null if not present
208 	 */
getPLTE()209 	public PngChunkPLTE getPLTE() {
210 		return (PngChunkPLTE) chunkList.getById1(PngChunkPLTE.ID);
211 	}
212 
213 	/**
214 	 * Creates a new empty palette chunk, queues it for write and return it to
215 	 * the caller, who should fill its entries
216 	 */
createPLTEChunk()217 	public PngChunkPLTE createPLTEChunk() {
218 		final PngChunkPLTE plte = new PngChunkPLTE(chunkList.imageInfo);
219 		queueChunk(plte);
220 		return plte;
221 	}
222 
223 	/**
224 	 * Returns the TRNS chunk, if present
225 	 *
226 	 * @return null if not present
227 	 */
getTRNS()228 	public PngChunkTRNS getTRNS() {
229 		return (PngChunkTRNS) chunkList.getById1(PngChunkTRNS.ID);
230 	}
231 
232 	/**
233 	 * Creates a new empty TRNS chunk, queues it for write and return it to the
234 	 * caller, who should fill its entries
235 	 */
createTRNSChunk()236 	public PngChunkTRNS createTRNSChunk() {
237 		final PngChunkTRNS trns = new PngChunkTRNS(chunkList.imageInfo);
238 		queueChunk(trns);
239 		return trns;
240 	}
241 
242 }
243