Quantcast

Sanselan support for writing IPTC fields

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Sanselan support for writing IPTC fields

László Fenyvesi
Dear Friends,
I'm using Christopher Blunck's code to copy iptc/exif metadata:
http://mail-archives.apache.org/mod_mbox/incubator-sanselan-dev/200811.mbox/%3C5609ED95-E659-4DE7-9EA0-EA2DBB76EE98@...%3E
(Sanselan=Imaging)
It usually works fine but fails on a lot of jpgs.
E.g.:
Enriched jpg:
     Title: 'Mouth checkup'
     Subject: 'Close-up of patient’s open mouth before oral checkup with
mirror near by'
Result jpg:
     Title: 'Close-up of patient?s open mouth before oral checkup with
mirror near by'
     Subject: 'Close-up of patient?s open mouth before oral checkup with
mirror near by'

Here are the pics:
http://movelex.hu/fenyoapa/user_commons_apache_org_imaging/iptc_exif_copy.html

As you can see the result's Title and Subject became the same and ’
became ?.

Can anybody tell me how to fix this?
Thank you.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Sanselan support for writing IPTC fields

Joakim Knudsen
Hi 

I'm also using Sanselan to read and write both IPTC and EXIF data (in an Android app setting).
Seems to be working as it should, but maybe we can help each other out if I share my code? 

I did a bit of research on EXIF tags, and got help from Phil Harvey at http://www.sno.phy.queensu.ca/~phil/exiftool/index.html
I've also got a lot of hassle dealing with charsets, encoding and "international characters" since I'm working from a Norwegian locale. Letters like "æ", "ø", and "å" required extra care. What I learned was to set UTF-16 encoding, and make sure to add proper Unicode markers to the text to write:
byte[] unicodeMarker = new byte[]{ 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00 };
byte[] comment = textToSet.getBytes(ENCODING_UTF16); // OR UTF-16BE if the file is big-endian!
byte[] bytesComment = new byte[unicodeMarker.length + comment.length];
System.arraycopy(unicodeMarker, 0, bytesComment, 0, unicodeMarker.length);
System.arraycopy(comment, 0, bytesComment, unicodeMarker.length, comment.length);
TiffOutputField exif_comment = new TiffOutputField(TiffConstants.EXIF_TAG_USER_COMMENT,
TiffFieldTypeConstants.FIELD_TYPE_UNDEFINED, bytesComment.length, bytesComment);
You can read more here: http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,7273.msg36826.html#msg36826



Since your question is on IPTC fields, I'll share the code I'm using for "mirroring" tags in IPTC fields (I support both EXIF and IPTC, and maybe later XMP). I have not done nearly as much research on IPTC as I did on EXIF, so the following code is entirely based on sample/example code I found online. It does seem to work, though

If you fix your problems, probably we can improve the code together? Please share what you learn!

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import android.content.ContentValues;
import android.util.Log;

import com.example.android.galleri.app.data.PhotoContract;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceFile;
import org.apache.sanselan.formats.jpeg.JpegImageMetadata;
import org.apache.sanselan.formats.jpeg.JpegImageParser;
import org.apache.sanselan.formats.jpeg.JpegPhotoshopMetadata;
import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter;
import org.apache.sanselan.formats.jpeg.iptc.IPTCRecord;
import org.apache.sanselan.formats.jpeg.iptc.IPTCType;
import org.apache.sanselan.formats.jpeg.iptc.JpegIptcRewriter;
import org.apache.sanselan.formats.jpeg.iptc.PhotoshopApp13Data;
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
import org.apache.sanselan.formats.tiff.constants.TiffFieldTypeConstants;
import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
import org.apache.sanselan.formats.tiff.write.TiffOutputField;
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
import org.apache.sanselan.util.IOUtils;

public class ExifWriter
{

private static String ENCODING_UTF8 = "UTF-8";
private static String ENCODING_UTF16 = "UTF-16LE";

private static boolean WRITE_IPTC_DATA = true;

/**
* This example illustrates how to add/update EXIF metadata in a JPEG file.
*
* @param jpegImageFile
* A source image file.
* @param values
* tags to set + value
* @throws IOException
* @throws ImageReadException
* @throws ImageWriteException
*/
public static void changeExifMetadata(File jpegImageFile, ContentValues values)
throws IOException, ImageReadException, ImageWriteException
{

// read source file into byte array, and use another impl. of updateExifMetadataLossless()
byte[] data = FileUtils.readFileToByteArray(jpegImageFile);
// NB! Make sure to read file (create byte array) BEFORE opening output stream (to same file)!

OutputStream os = null;
try
{
TiffOutputSet outputSet = null;

// note that metadata might be null if no metadata is found.
IImageMetadata metadata = Sanselan.getMetadata(jpegImageFile);
JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
JpegPhotoshopMetadata PSmetadata = ((JpegImageMetadata) metadata).getPhotoshop();

if (null != jpegMetadata)
{
// note that exif might be null if no Exif metadata is found.
TiffImageMetadata exif = jpegMetadata.getExif();

if (null != exif)
{
// TiffImageMetadata class is immutable (read-only).
// TiffOutputSet class represents the Exif data to write.
//
// Usually, we want to update existing Exif metadata by
// changing
// the values of a few fields, or adding a field.
// In these cases, it is easiest to use getOutputSet() to
// start with a "copy" of the fields read from the image.
outputSet = exif.getOutputSet();
}
}


ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
if (data != null)
new ExifRewriter().updateExifMetadataLossless(data, outputStream, outputSet); // this closes outputstream (os)


ByteArrayOutputStream dest = new ByteArrayOutputStream();
if (!WRITE_IPTC_DATA) {
// copy outputStream to dest
dest = outputStream;

} else {

// http://www.massapi.com/class/org/apache/sanselan/formats/jpeg/iptc/JpegIptcRewriter.html
// showed how to initialize ListArrays (blocks, records etc) when the JPEG does not have an IPTC block yet
// 27/12-2016
PhotoshopApp13Data newData;
List<?> newBlocks = null;

Map params = new HashMap();
boolean ignoreImageData = false; //isPhilHarveyTestImage(imageFile);
boolean PARAM_KEY_READ_THUMBNAILS = false;
params
.put(PARAM_KEY_READ_THUMBNAILS, new Boolean(
!ignoreImageData));
// params.put(PARAM_KEY_VERBOSE, Boolean.TRUE);
// params.put(PARAM_KEY_VERBOSE, Boolean.TRUE);

if (PSmetadata != null)
newBlocks = PSmetadata.photoshopApp13Data.getNonIptcBlocks();
else
newBlocks = new ArrayList<>();

// begin by getting ALL existing records (unless PSmetadata == null) <-- meaning, no IPTC blocks (APP14) yet
List<IPTCRecord> newRecords;
List removeRecords = new ArrayList();
if (PSmetadata != null) {
newRecords = PSmetadata.photoshopApp13Data.getRecords();
} else {
newRecords = new ArrayList<IPTCRecord>();
}

for (Map.Entry<String, Object> item : values.valueSet()) {
String key = item.getKey(); // getting key
Object o = values.get(key); // may be null
if (o != null) {
String textToSet = "IPTC Caption/Abstract: " + o.toString();

switch (key) {
case PhotoContract.PhotoEntry.COLUMN_IPTC_CAPTION: {

IPTCType captionAbstract120 = new IPTCType(120, "Caption/Abstract");

if (PSmetadata != null) {
for (int j = 0; j < newRecords.size(); j++) {
IPTCRecord record = (IPTCRecord) newRecords.get(j);

if (record.iptcType.type == captionAbstract120.type) {
removeRecords.add(record);
}
}
}
newRecords.add(new IPTCRecord(captionAbstract120, textToSet));

break;
}

default:
break;
}
}
}

if (PSmetadata != null) {
// remove old tags:
for (int j = 0; j < removeRecords.size(); j++) {
IPTCRecord record = (IPTCRecord) removeRecords.get(j);
newRecords.remove(record);
}
}

newData = new PhotoshopApp13Data(newRecords, newBlocks);

// enrich the partially clothed byte[] with IPTC metadata
InputStream src = new ByteArrayInputStream(outputStream.toByteArray());

new JpegIptcRewriter().writeIPTC(src, dest, newData);

}

// write output stream "dest" to file
try {
os = new FileOutputStream(jpegImageFile);
os = new BufferedOutputStream(os);
dest.writeTo(os);
} catch(IOException ioe) {
// Handle exception here
ioe.printStackTrace();
} finally {
os.close();
}


} finally
{
if (os != null)
try
{
os.close();
} catch (IOException e)
{

}
}
}


}


Joakim


On 3 March 2017 at 14:46, László Fenyvesi <[hidden email]> wrote:
Dear Friends,
I'm using Christopher Blunck's code to copy iptc/exif metadata:
http://mail-archives.apache.org/mod_mbox/incubator-sanselan-dev/200811.mbox/%3C5609ED95-E659-4DE7-9EA0-EA2DBB76EE98@thebluncks.com%3E
(Sanselan=Imaging)
It usually works fine but fails on a lot of jpgs.
E.g.:
Enriched jpg:
    Title: 'Mouth checkup'
    Subject: 'Close-up of patient’s open mouth before oral checkup with mirror near by'
Result jpg:
    Title: 'Close-up of patient?s open mouth before oral checkup with mirror near by'
    Subject: 'Close-up of patient?s open mouth before oral checkup with mirror near by'

Here are the pics:
http://movelex.hu/fenyoapa/user_commons_apache_org_imaging/iptc_exif_copy.html

As you can see the result's Title and Subject became the same and ’ became ?.

Can anybody tell me how to fix this?
Thank you.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]


Loading...