Full working example available on GitHub: read-and-write-xmp-in-psd-ai-files-java

The Old Way Was Painful

Picture the archive cleanup nobody volunteers for. A folder of PSD masters and AI sources needs rights notices and keywords before it enters the DAM. The routine: open a file in Photoshop, open File Info, type the copyright, type the keywords, save, close, next file. Each save re-renders a layered file just to change a few strings of xmp metadata. The archive that taught me this lesson was a folder of untagged Illustrator files nobody could search; we fixed it with a loop, not with more patience.

Multiply the routine by an archive and it stops being a task and becomes a project. Worse, it is unauditable: nobody can prove afterward which files were done, and the ones that were skipped look identical until a licensing question finds them. The panel route also quietly couples data entry to design tooling. Whoever fixes metadata needs an Adobe seat, a workstation that opens layered masters comfortably, and the patience to wait for saves that re-render artwork just to change strings.

The real cost of doing it manually: metadata fixed by hand is metadata nobody can verify later; the process leaves no trail except tired designers.

There’s a Better Way

GroupDocs.Metadata for Java reads and writes the XMP packet directly. Cast getRootPackage() to IXmp and the packet, its schemes, and its arrays are ordinary Java objects, identical for PSD and AI containers, no Adobe software involved. The documentation lists 170+ formats behind the same API.

Before we start, you’ll need:

  • JDK 8 or later with Maven
  • GroupDocs.Metadata for Java 24.7 (get a temporary license)
  • A PSD or AI file to practice on

Add the dependency and the GroupDocs repository to your pom.xml:

mvn dependency:get -Dartifact=com.groupdocs:groupdocs-metadata:24.7

The companion repository ships a ready pom.xml plus seeded samples of both formats, and asserts every step below.

The New Way: Four Operations in Java

Step 1 — See What a File Carries

The snapshot dumps the packet and every scheme into one LinkedHashMap, preserving the order the file declares.

// Snapshot the packet, then sweep for anything the schemes missed
Map<String, String> result = new LinkedHashMap<>();
try (Metadata metadata = new Metadata(adobeFilePath)) {
    IXmp root = (IXmp) metadata.getRootPackage();
    if (root != null && root.getXmpPackage() != null) {
        for (MetadataProperty p : root.getXmpPackage()) {
            put(result, p);
        }
        XmpSchemes schemes = root.getXmpPackage().getSchemes();
        collect(result, schemes.getDublinCore());
        collect(result, schemes.getPhotoshop());
        collect(result, schemes.getXmpBasic());
        collect(result, schemes.getCameraRaw());
    }
    for (MetadataProperty p : metadata.findProperties(new NamedPropertySpec())) {
        if (!result.containsKey(p.getName())) put(result, p);
    }
}
return result;

The small put and collect helpers prefer getInterpretedValue() so dates arrive readable, and the Specification-driven sweep catches vendor packets. The repository version walks seven schemes; the shape stays the same.

Step 2 — Read the Fields That Answer Questions

Licensing asks about dc:rights. Search cares about dc:subject. Both live in Dublin Core, and the scoped read costs nine fields, not a tree walk.

// dc:* only - the interoperability fields DAM systems agree on
XmpDublinCorePackage dc = root.getXmpPackage().getSchemes().getDublinCore();
if (dc == null) return result;
for (MetadataProperty p : dc) {
    String value = "";
    if (p.getInterpretedValue() != null
            && p.getInterpretedValue().getRawValue() != null) {
        value = String.valueOf(p.getInterpretedValue().getRawValue());
    } else if (p.getValue() != null && p.getValue().getRawValue() != null) {
        value = String.valueOf(p.getValue().getRawValue());
    }
    result.put(p.getName(), value);
}

The Photoshop scheme works the same way through typed getters (getCity(), getCredit(), getColorMode() and five more), covering the psd metadata fields Bridge and Lightroom filters read. In the repository that reader wraps every getter with a null-safe helper, so a sparsely populated file returns empty strings instead of surprises. That detail matters more than it looks: the whole point of automating an archive is that odd files flow through instead of stopping the loop.

Both scoped reads share a cost profile worth naming. One file open, one scheme, no tree walk. Put them in request handlers and gates; save the full snapshot for ingestion jobs that store everything.

Step 3 — Stamp and Tag Without Opening Adobe

The writers guard-create anything missing, which makes them safe for fresh exports carrying no packet at all.

// Create missing layers, then write rights, creator, and CreatorTool
if (root.getXmpPackage() == null) {
    root.setXmpPackage(new XmpPacketWrapper());
}
if (root.getXmpPackage().getSchemes().getDublinCore() == null) {
    root.getXmpPackage().getSchemes().setDublinCore(new XmpDublinCorePackage());
}
XmpDublinCorePackage dc = root.getXmpPackage().getSchemes().getDublinCore();
dc.setRights(copyright);
dc.set("dc:creator", XmpArray.from(new String[]{creator}, XmpArrayType.Ordered));

if (root.getXmpPackage().getSchemes().getXmpBasic() == null) {
    root.getXmpPackage().getSchemes().setXmpBasic(new XmpBasicPackage());
}
root.getXmpPackage().getSchemes().getXmpBasic().setCreatorTool(creator);

metadata.save(outputPath);

Keywords follow the same pattern with one call, writing the whole bag as an Unordered array:

// Replace the dc:subject bag - merge in Java first for additive tagging
root.getXmpPackage().getSchemes().getDublinCore().set(
        "dc:subject", XmpArray.from(keywords, XmpArrayType.Unordered));
metadata.save(outputPath);

Main.java closes the loop: it re-reads the outputs and asserts the copyright string and the first keyword actually survived the save.

That closing assert deserves a sentence of advocacy. Metadata writes fail quietly when they fail; the file saves, the bytes change, and the value you meant to write is simply not there because a scheme object was stale or a path pointed at the original. A read-back after every write run costs one extra open per file and converts “the script finished” into “the values are present”, which is the statement an archive owner actually wants. Keep it in production, not just in the demo.

How do keywords actually make assets findable?

Search tools do not read pixels; they read dc:subject. Bridge, DAM indexers, and stock platforms treat that bag as the asset’s vocabulary, so a file without keywords simply never matches a query. Writing the bag as an Unordered XmpArray, the way AddKeywords does, is what moves an asset from invisible to findable, and the write costs one save.

Side-by-Side: Before vs. After

Before (panel editing) After (Java pipeline)
Tooling Photoshop or Bridge per file One Maven project, no Adobe seat
Coverage Fields the panel exposes Every scheme plus vendor packets
Repeatability Depends on who clicked Same loop, same result, auditable
XMP-less files Panel behavior varies Guards create the packet and schemes
Verification Trust Asserted read-back per file

The verification row decides it for archives: a script that proves its own writes is the difference between “we tagged the files” and “we can show you”.

Real-World Example: The Agency Handoff

A studio receives mixed PSD and AI deliveries from three agencies, each with its own metadata discipline. Their intake job now runs the snapshot on arrival, flags files whose dc:rights read empty, stamps them with the contracted rights line, and writes the campaign keyword set. The same loop serves both formats because nothing in the code names a container, and adobe illustrator metadata that arrives blank leaves the intake tagged and searchable.

The follow-on effect is the part the studio did not predict: agency scorecards. Because the intake job logs which deliveries arrived with empty rights fields, procurement now sees which suppliers ship clean metadata and which rely on the client to fix it. The conversation with the worst offender took one chart.

What Else Can You Do with GroupDocs.Metadata?

  • EXIF and IPTC in the same files: PSDs carry three metadata standards; the same library reads the other two through their own packages, so a full asset profile is two more reads away.
  • 170+ other formats: the identical Metadata entry point serves Office, PDF, audio, and video files, which is how one intake job ends up covering a whole mixed archive.
  • Property search: findProperties with a Specification sweeps any file for whatever predicate you define, from rights checks to custom-field hunts.

Conclusion

What used to be a per-file panel routine is now four Java operations: snapshot, scoped read, ownership stamp, keyword write. The same code serves PSD and AI files, guards make empty exports safe, and the asserted sample project proves the whole set before you point it at a real archive.

Ready to retire the File Info panel?

Additional Resources