001/*
002 * Copyright (C) 2014 Konik.io
003 *
004 * This file is part of Konik library.
005 *
006 * Konik library is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU Affero General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * Konik library is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU Affero General Public License for more details.
015 *
016 * You should have received a copy of the GNU Affero General Public License
017 * along with Konik library.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package io.konik.itext.xmp;
020
021import static io.konik.itext.xmp.XmpZfNs.ZF_NS;
022import static io.konik.itext.xmp.XmpZfNs.ZF_NS_ALT;
023import io.konik.exception.TransformationWarning;
024
025import java.io.ByteArrayInputStream;
026
027import javax.inject.Named;
028import javax.inject.Singleton;
029import javax.xml.stream.XMLEventReader;
030import javax.xml.stream.XMLInputFactory;
031import javax.xml.stream.XMLStreamException;
032import javax.xml.stream.events.XMLEvent;
033
034import com.itextpdf.text.pdf.PdfReader;
035
036/**
037 * This Class is responsible for extracting {@link ZfXmpInfo} from a given iText {@link PdfReader}.
038 */
039@Named
040@Singleton
041public class XmpExtractor {
042
043   private static final String ZF = "zf";
044
045   /**
046    * Extract ZUGFeRD info.
047    *
048    * @param xmpContent the xmp xml conent
049    * @return the extracted information from xmp content
050    * @throws TransformationWarning the transformation warning
051    */
052   public ZfXmpInfo extract(byte[] xmpContent) throws TransformationWarning {
053      try {
054         return extractIntern(xmpContent);
055      } catch (XMLStreamException e) {
056        throw new TransformationWarning("Could not handle XML: "+ e.getLocalizedMessage(),e);
057      }
058   }
059
060   private ZfXmpInfo extractIntern(byte[] xmpConent) throws TransformationWarning, XMLStreamException {
061      ZfXmpInfo zfInfo = new ZfXmpInfo();
062      
063      XMLInputFactory inputFactory = XMLInputFactory.newInstance();
064      XMLEventReader xmlEventReader = inputFactory.createXMLEventReader(new ByteArrayInputStream(xmpConent));
065    
066      while (xmlEventReader.hasNext()) {
067         XMLEvent event = xmlEventReader.nextEvent();
068         if (event.isStartElement() && hasZfNsContext(event)) {
069            String localPart = event.asStartElement().getName().getLocalPart();
070            String content = xmlEventReader.nextEvent().asCharacters().toString();
071            
072            if (localPart.equals("ConformanceLevel")) {
073               zfInfo.setConformanceLevel(content);
074            }
075            if (localPart.equals("DocumentFileName")) {
076               zfInfo.setDocumentFileName(content);
077            }
078            if (localPart.equals("DocumentType")) {
079               zfInfo.setDocumentType(content);
080            }
081            if (localPart.equals("Version")) {
082               zfInfo.setVersion(content);
083            }
084         }
085      }
086      if (!zfInfo.isValid()) {
087         throw new TransformationWarning("No ZF information availible within XMP data");
088      }
089     return zfInfo;
090   }
091
092   private boolean hasZfNsContext(XMLEvent event) {
093      return hasZfNsContext(event,ZF_NS) || hasZfNsContext(event,ZF_NS_ALT);
094   }
095   
096   private boolean hasZfNsContext(XMLEvent event, XmpZfNs nsPrexix) {
097      return ZF.equals(event.asStartElement().getNamespaceContext().getPrefix(nsPrexix.value));
098   }
099}