Exemplo completo em funcionamento disponível no GitHub:
read-and-write-xmp-in-psd-ai-files-java

O jeito antigo era doloroso

Imagine a limpeza de arquivos que ninguém se oferece para fazer. Uma pasta de masters em PSD e fontes em AI precisa de avisos de direitos e palavras‑chave antes de entrar no DAM. A rotina: abrir um arquivo no Photoshop, abrir File Info, digitar o copyright, digitar as palavras‑chave, salvar, fechar, próximo arquivo. Cada salvamento re‑renderiza um arquivo em camadas apenas para mudar algumas strings de metadados XMP. O arquivo que me ensinou essa lição era uma pasta de arquivos Illustrator sem tags que ninguém conseguia pesquisar; resolvemos isso com um loop, não com mais paciência.

Multiplique a rotina por um arquivo e ela deixa de ser uma tarefa e passa a ser um projeto. Pior, torna‑se não auditável: ninguém pode provar depois quais arquivos foram processados, e os que foram pulados parecem idênticos até que uma questão de licenciamento os descubra. A abordagem via painel também acopla silenciosamente a entrada de dados às ferramentas de design. Quem corrige metadados precisa de uma licença Adobe, de uma estação de trabalho que abra masters em camadas confortavelmente e da paciência para esperar salvamentos que re‑renderizam a arte apenas para mudar strings.

O custo real de fazer isso manualmente: metadados corrigidos à mão são metadados que ninguém pode verificar depois; o processo não deixa rastro, exceto designers cansados.

Existe um jeito melhor

GroupDocs.Metadata para Java lê e grava o pacote XMP diretamente. Converta getRootPackage() para IXmp e o pacote, seus esquemas e seus arrays são objetos Java comuns, idênticos para contêineres PSD e AI, sem software Adobe envolvido. A documentação lista mais de 170 formatos por trás da mesma API.

Antes de começar, você precisará:

Adicione a dependência e o repositório GroupDocs ao seu pom.xml:

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

O repositório complementar fornece um pom.xml pronto, além de amostras de ambos os formatos, e valida cada passo abaixo.

O novo jeito: quatro operações em Java

Etapa 1 – Veja o que um arquivo carrega

O instantâneo despeja o pacote e cada esquema em um único LinkedHashMap, preservando a ordem declarada pelo arquivo.

// 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;

Os pequenos auxiliares put e collect preferem getInterpretedValue() para que datas cheguem legíveis, e a varredura guiada por Specification captura pacotes de fornecedores. A versão do repositório percorre sete esquemas; a estrutura permanece a mesma.

Etapa 2 – Leia os campos que respondem a perguntas

Licenciamento pergunta sobre dc:rights. Busca se importa com dc:subject. Ambos vivem em Dublin Core, e a leitura escopada custa nove campos, não uma travessia de árvore.

// 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);
}

O esquema Photoshop funciona da mesma forma através de getters tipados (getCity(), getCredit(), getColorMode() e mais cinco), cobrindo os campos de metadados PSD que filtros do Bridge e Lightroom leem. No repositório esse leitor envolve cada getter com um auxiliar que trata nulos, de modo que um arquivo pouco preenchido devolve strings vazias em vez de surpresas. Esse detalhe importa mais do que parece: o objetivo de automatizar um arquivo é que arquivos estranhos fluam ao invés de interromper o loop.

Ambas as leituras escopadas compartilham um perfil de custo que vale a pena nomear. Um arquivo aberto, um esquema, sem travessia de árvore. Coloque‑as em manipuladores de requisição e portas; salve o instantâneo completo para trabalhos de ingestão que armazenam tudo.

Etapa 3 – Carimbe e marque sem abrir o Adobe

Os escritores criam guardas para tudo que falta, o que os torna seguros para exportações novas que não carregam nenhum pacote.

// 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);

Palavras‑chave seguem o mesmo padrão com uma única chamada, gravando todo o bag como um array Unordered:

// 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 fecha o loop: ele relê as saídas e verifica se a string de copyright e a primeira palavra‑chave realmente sobreviveram ao salvamento.

Essa verificação final merece uma frase de defesa. Gravações de metadados falham silenciosamente quando falham; o arquivo salva, os bytes mudam, e o valor que você pretendia gravar simplesmente não está lá porque um objeto de esquema estava desatualizado ou um caminho apontava para o original. Uma leitura de volta após cada gravação custa uma abertura extra por arquivo e transforma “o script terminou” em “os valores estão presentes”, que é a afirmação que um proprietário de arquivo realmente quer. Mantenha isso em produção, não apenas na demonstração.

Como as palavras‑chave realmente tornam os ativos encontráveis?

Ferramentas de busca não leem pixels; leem dc:subject. Bridge, indexadores de DAM e plataformas de estoque tratam esse bag como o vocabulário do ativo, de modo que um arquivo sem palavras‑chave nunca corresponde a uma consulta. Gravar o bag como um Unordered XmpArray, como faz AddKeywords, é o que move um ativo de invisível para encontrável, e a gravação custa um salvamento.

Lado a lado: antes vs. depois

Antes (edição via painel) Depois (pipeline Java)
Ferramentas Photoshop ou Bridge por arquivo Um projeto Maven, sem licença Adobe
Cobertura Campos que o painel expõe Todos os esquemas mais pacotes de fornecedores
Repetibilidade Depende de quem clicou Mesmo loop, mesmo resultado, auditável
Arquivos sem XMP Comportamento do painel varia Guardas criam o pacote e os esquemas
Verificação Confiança Leitura de volta assertiva por arquivo

A linha de verificação decide para arquivos: um script que prova suas próprias gravações é a diferença entre “marcamos os arquivos” e “podemos mostrar isso a você”.

Exemplo real: a entrega da agência

Um estúdio recebe entregas mistas de PSD e AI de três agências, cada uma com sua própria disciplina de metadados. Seu job de ingestão agora executa o instantâneo na chegada, sinaliza arquivos cujo dc:rights está vazio, carimba‑os com a linha de direitos contratada e grava o conjunto de palavras‑chave da campanha. O mesmo loop serve ambos os formatos porque nada no código menciona um contêiner, e metadados do Adobe Illustrator que chegam em branco deixam a ingestão marcada e pesquisável.

O efeito colateral é a parte que o estúdio não previu: scorecards de agências. Como o job de ingestão registra quais entregas chegaram com campos de direitos vazios, a área de compras agora vê quais fornecedores enviam metadados limpos e quais dependem do cliente para corrigi‑los. A conversa com o maior infrator levou apenas um gráfico.

O que mais você pode fazer com GroupDocs.Metadata?

  • EXIF e IPTC nos mesmos arquivos: PSDs carregam três padrões de metadados; a mesma biblioteca lê os outros dois por meio de seus próprios pacotes, de modo que um perfil completo de ativo está a duas leituras de distância.
  • 170+ outros formatos: o ponto de entrada idêntico Metadata serve Office, PDF, áudio e vídeo, que é como um job de ingestão cobre um arquivo misto inteiro.
  • Busca por propriedade: findProperties com uma Specification varre qualquer arquivo para o predicado que você definir, de verificações de direitos a buscas de campos personalizados.

Conclusão

O que antes era uma rotina de painel por arquivo agora são quatro operações Java: instantâneo, leitura escopada, carimbo de propriedade e gravação de palavras‑chave. O mesmo código serve arquivos PSD e AI, guardas tornam exportações vazias seguras, e o projeto de amostra assertivo comprova tudo antes de apontá‑lo para um arquivo real.

Pronto para aposentar o painel File Info?

Recursos adicionais