Package org.apache.padaf.preflight.utils

Source Code of org.apache.padaf.preflight.utils.ContentStreamEngine

/*****************************************************************************
*
* 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.
*
****************************************************************************/

package org.apache.padaf.preflight.utils;

import static org.apache.padaf.preflight.ValidationConstants.ERROR_GRAPHIC_INVALID_COLOR_SPACE_CMYK;
import static org.apache.padaf.preflight.ValidationConstants.ERROR_GRAPHIC_INVALID_COLOR_SPACE_MISSING;
import static org.apache.padaf.preflight.ValidationConstants.ERROR_GRAPHIC_INVALID_COLOR_SPACE_RGB;
import static org.apache.padaf.preflight.ValidationConstants.ERROR_GRAPHIC_TOO_MANY_GRAPHIC_STATES;
import static org.apache.padaf.preflight.ValidationConstants.ERROR_GRAPHIC_UNEXPECTED_VALUE_FOR_KEY;
import static org.apache.padaf.preflight.ValidationConstants.ERROR_SYNTAX_STREAM_INVALID_FILTER;
import static org.apache.padaf.preflight.ValidationConstants.ERROR_SYNTAX_STREAM_UNDEFINED_FILTER;
import static org.apache.padaf.preflight.ValidationConstants.MAX_GRAPHIC_STATES;
import static org.apache.padaf.preflight.ValidationConstants.STREAM_DICTIONARY_KEY_COLOR_SPACE;
import static org.apache.padaf.preflight.ValidationConstants.STREAM_DICTIONARY_KEY_F;
import static org.apache.padaf.preflight.ValidationConstants.STREAM_DICTIONARY_KEY_FILTER;

import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.padaf.preflight.DocumentHandler;
import org.apache.padaf.preflight.ValidationException;
import org.apache.padaf.preflight.ValidationResult.ValidationError;
import org.apache.padaf.preflight.contentstream.ContentStreamException;
import org.apache.padaf.preflight.contentstream.StubOperator;
import org.apache.padaf.preflight.graphics.ICCProfileWrapper;
import org.apache.padaf.preflight.graphics.color.ColorSpaceHelper;
import org.apache.padaf.preflight.graphics.color.ColorSpaceHelperFactory;
import org.apache.padaf.preflight.graphics.color.ColorSpaceHelperFactory.ColorSpaceRestriction;
import org.apache.padaf.preflight.graphics.color.ColorSpaces;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSDocument;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdmodel.graphics.color.PDCalGray;
import org.apache.pdfbox.pdmodel.graphics.color.PDCalRGB;
import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
import org.apache.pdfbox.pdmodel.graphics.color.PDColorState;
import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased;
import org.apache.pdfbox.pdmodel.graphics.color.PDLab;
import org.apache.pdfbox.util.PDFOperator;
import org.apache.pdfbox.util.PDFStreamEngine;
import org.apache.pdfbox.util.operator.BeginText;
import org.apache.pdfbox.util.operator.Concatenate;
import org.apache.pdfbox.util.operator.EndText;
import org.apache.pdfbox.util.operator.GRestore;
import org.apache.pdfbox.util.operator.GSave;
import org.apache.pdfbox.util.operator.Invoke;
import org.apache.pdfbox.util.operator.MoveText;
import org.apache.pdfbox.util.operator.MoveTextSetLeading;
import org.apache.pdfbox.util.operator.NextLine;
import org.apache.pdfbox.util.operator.OperatorProcessor;
import org.apache.pdfbox.util.operator.SetCharSpacing;
import org.apache.pdfbox.util.operator.SetHorizontalTextScaling;
import org.apache.pdfbox.util.operator.SetLineCapStyle;
import org.apache.pdfbox.util.operator.SetLineDashPattern;
import org.apache.pdfbox.util.operator.SetLineJoinStyle;
import org.apache.pdfbox.util.operator.SetLineWidth;
import org.apache.pdfbox.util.operator.SetMatrix;
import org.apache.pdfbox.util.operator.SetNonStrokingCMYKColor;
import org.apache.pdfbox.util.operator.SetNonStrokingColor;
import org.apache.pdfbox.util.operator.SetNonStrokingColorSpace;
import org.apache.pdfbox.util.operator.SetNonStrokingRGBColor;
import org.apache.pdfbox.util.operator.SetStrokingCMYKColor;
import org.apache.pdfbox.util.operator.SetStrokingColor;
import org.apache.pdfbox.util.operator.SetStrokingColorSpace;
import org.apache.pdfbox.util.operator.SetStrokingRGBColor;
import org.apache.pdfbox.util.operator.SetTextFont;
import org.apache.pdfbox.util.operator.SetTextLeading;
import org.apache.pdfbox.util.operator.SetTextRenderingMode;
import org.apache.pdfbox.util.operator.SetTextRise;
import org.apache.pdfbox.util.operator.SetWordSpacing;
/**
* This class inherits from org.apache.pdfbox.util.PDFStreamEngine to allow the
* validation of specific rules in ContentStream.
*/
public abstract class ContentStreamEngine extends PDFStreamEngine {

  private enum ColorSpaceType {
    RGB,
    CMYK,
    ALL;
  }

  protected DocumentHandler documentHandler = null;

  protected Map<String,OperatorProcessor> contentStreamEngineOperators = new HashMap<String,OperatorProcessor>();

  public ContentStreamEngine(DocumentHandler _handler) {
    this.documentHandler = _handler;

    // ---- Graphics operators
    registerOperatorProcessor("w", new SetLineWidth());
    registerOperatorProcessor("cm", new Concatenate());

    registerOperatorProcessor("CS", new SetStrokingColorSpace());
    registerOperatorProcessor("cs", new SetNonStrokingColorSpace());
    registerOperatorProcessor("d", new SetLineDashPattern());
    registerOperatorProcessor("Do", new Invoke());

    registerOperatorProcessor("j", new SetLineJoinStyle());
    registerOperatorProcessor("J", new SetLineCapStyle());
    registerOperatorProcessor("K", new SetStrokingCMYKColor());
    registerOperatorProcessor("k", new SetNonStrokingCMYKColor());

    registerOperatorProcessor("rg", new SetNonStrokingRGBColor());
    registerOperatorProcessor("RG", new SetStrokingRGBColor());

    registerOperatorProcessor("SC", new SetStrokingColor());
    registerOperatorProcessor("SCN", new SetStrokingColor());
    registerOperatorProcessor("sc", new SetNonStrokingColor());
    registerOperatorProcessor("scn", new SetNonStrokingColor());

    // ---- Graphics state
    registerOperatorProcessor("Q", new GRestore());
    registerOperatorProcessor("q", new GSave());

    // ---- Text operators
    registerOperatorProcessor("BT", new BeginText());
    registerOperatorProcessor("ET", new EndText());
    registerOperatorProcessor("Tf", new SetTextFont());
    registerOperatorProcessor("Tr", new SetTextRenderingMode());
    registerOperatorProcessor("Tm", new SetMatrix());
    registerOperatorProcessor("Td", new MoveText());
    registerOperatorProcessor("T*", new NextLine());
    registerOperatorProcessor("TD", new MoveTextSetLeading());
    registerOperatorProcessor("Tc", new SetCharSpacing());
    registerOperatorProcessor("TL", new SetTextLeading());
    registerOperatorProcessor("Ts", new SetTextRise());
    registerOperatorProcessor("Tw", new SetWordSpacing());
    registerOperatorProcessor("Tz", new SetHorizontalTextScaling());

    // ---- Do not use the PDFBox Operator, because of the PageDrawer class cast
    // Or because the Operator doesn't exist
    StubOperator so = new StubOperator();
    registerOperatorProcessor("l", so);
    registerOperatorProcessor("re", so);
    registerOperatorProcessor("c", so);
    registerOperatorProcessor("y", so);
    registerOperatorProcessor("v", so);
    registerOperatorProcessor("n", so);
    registerOperatorProcessor("BI", so);
    registerOperatorProcessor("EI", so);
    registerOperatorProcessor("m", so);
    registerOperatorProcessor("W*", so);
    registerOperatorProcessor("W", so);
    registerOperatorProcessor("h", so);

    registerOperatorProcessor("Tj", so);
    registerOperatorProcessor("TJ", so);
    registerOperatorProcessor("'", so);
    registerOperatorProcessor("\"", so);

    registerOperatorProcessor("b", so);
    registerOperatorProcessor("B", so);
    registerOperatorProcessor("b*", so);
    registerOperatorProcessor("B*", so);

    registerOperatorProcessor("BDC", so);
    registerOperatorProcessor("BMC", so);
    registerOperatorProcessor("DP", so);
    registerOperatorProcessor("EMC", so);

    registerOperatorProcessor("d0", so);
    registerOperatorProcessor("d1", so);

    registerOperatorProcessor("f", so);
    registerOperatorProcessor("F", so);
    registerOperatorProcessor("f*", so);

    registerOperatorProcessor("g", so);
    registerOperatorProcessor("G", so);

    registerOperatorProcessor("M", so);
    registerOperatorProcessor("MP", so);

    registerOperatorProcessor("gs", so);
    registerOperatorProcessor("h", so);
    registerOperatorProcessor("i", so);

    registerOperatorProcessor("ri", so);
    registerOperatorProcessor("s", so);
    registerOperatorProcessor("S", so);
    registerOperatorProcessor("sh", so);
  }

  public final void registerOperatorProcessor( String operator, OperatorProcessor op )
  {
    super.registerOperatorProcessor(operator, op);
    contentStreamEngineOperators.put( operator, op );
  }


  /**
   * Check operands of the "ri" operator. Operands must exist in the
   * RenderingIntent list. (net.awl.edoc.pdfa.validation.utils.RenderingIntents)
   *
   * @param operator
   *          the "ri" operator
   * @param arguments
   *          the "ri" operands
   * @throws ContentStreamException
   *           ERROR_GRAPHIC_UNEXPECTED_VALUE_FOR_KEY if the operand is invalid
   */
  protected void validRenderingIntent(PDFOperator operator, List arguments)
      throws ContentStreamException {
    if ("ri".equals(operator.getOperation())) {
      String riArgument0 = "";
      if (arguments.get(0) instanceof COSName) {
        riArgument0 = ((COSName)arguments.get(0)).getName();
      } else if (arguments.get(0) instanceof String) {
        riArgument0 = (String)arguments.get(0);
      }

      if (!RenderingIntents.contains(riArgument0)) {
        throwContentStreamException("Unexpected value '" + arguments.get(0)
            + "' for ri operand. ", ERROR_GRAPHIC_UNEXPECTED_VALUE_FOR_KEY);
      }
    }
  }

  /**
   * Valid the number of graphic states if the operator is the Save Graphic state operator ("q")
   * @param operator
   * @throws ContentStreamException
   */
  protected void validNumberOfGraphicStates(PDFOperator operator) throws ContentStreamException  {
    if ("q".equals(operator.getOperation())) {
      int numberOfGraphicStates = this.getGraphicsStack().size();
      if (numberOfGraphicStates > MAX_GRAPHIC_STATES) {
        throwContentStreamException("Too many graphic states", ERROR_GRAPHIC_TOO_MANY_GRAPHIC_STATES);
      }
    }
  }

  /**
   * Throw a ContentStreamException if the LZW filter is used in a InlinedImage.
   *
   * @param operator
   *          the InlinedImage object (BI to EI)
   * @throws ContentStreamException
   */
  protected void validImageFilter(PDFOperator operator)
      throws ContentStreamException {
    COSDictionary dict = operator.getImageParameters().getDictionary();
    // ---- Search a Filter declaration in the InlinedImage dictionary.
    // ---- The LZWDecode Filter is forbidden.
    String filter = dict.getNameAsString(STREAM_DICTIONARY_KEY_F);
    if (filter == null) {
      filter = dict.getNameAsString(STREAM_DICTIONARY_KEY_FILTER);
    }

    String errorCode = FilterHelper.isAuthorizedFilter(filter);
    if (errorCode != null) {
      // --- LZW is forbidden.
      if ( ERROR_SYNTAX_STREAM_INVALID_FILTER.equals(errorCode) ) {
        throwContentStreamException("LZW filter can't be used in a PDF/A File", ERROR_SYNTAX_STREAM_INVALID_FILTER);
      } else {
        throwContentStreamException("This filter isn't defined in the PDF Reference Third Edition.", ERROR_SYNTAX_STREAM_UNDEFINED_FILTER);
      }
    }
  }

  /**
   * This method validates if the ColorSpace used by the InlinedImage is
   * consistent with the color space defined in OutputIntent dictionaries.
   *
   * @param operator
   *          the InlinedImage object (BI to EI)
   * @throws ContentStreamException
   */
  protected void validImageColorSpace(PDFOperator operator)
      throws ContentStreamException, IOException {
    COSDictionary dict = operator.getImageParameters().getDictionary();

    COSDocument doc = this.documentHandler.getDocument().getDocument();
    COSBase csInlinedBase = dict.getItem(COSName
        .getPDFName(STREAM_DICTIONARY_KEY_COLOR_SPACE));

    ColorSpaceHelper csHelper = null;
    if (csInlinedBase != null) {

      if (COSUtils.isString(csInlinedBase, doc)) {
        // ---- In InlinedImage only DeviceGray/RGB/CMYK and restricted Indexed
        // color spaces
        // are allowed.
        String colorSpace = COSUtils.getAsString(csInlinedBase, doc);
        ColorSpaces cs = null;

        try {
          cs = ColorSpaces.valueOf(colorSpace);
        } catch (IllegalArgumentException e) {
          // ---- The color space is unknown.
          // ---- Try to access the resources dictionary, the color space can be
          // a reference.
          PDColorSpace pdCS = (PDColorSpace) this.getColorSpaces().get(
              colorSpace);
          if (pdCS != null) {
            cs = ColorSpaces.valueOf(pdCS.getName());
            csHelper = ColorSpaceHelperFactory.getColorSpaceHelper(pdCS,
                documentHandler, ColorSpaceRestriction.ONLY_DEVICE);
          }
        }

        if (cs == null) {
          throwContentStreamException("The ColorSpace is unknown",
              ERROR_GRAPHIC_UNEXPECTED_VALUE_FOR_KEY);
        }
      }

      if (csHelper == null) {
        csHelper = ColorSpaceHelperFactory.getColorSpaceHelper(csInlinedBase,
            documentHandler, ColorSpaceRestriction.ONLY_DEVICE);
      }
      List<ValidationError> errors = new ArrayList<ValidationError>();
      try {
        if (!csHelper.validate(errors)) {
          ValidationError ve = errors.get(0);
          throwContentStreamException(ve.getDetails(), ve.getErrorCode());
        }
      } catch (ValidationException e) {
        throw new IOException(e.getMessage());
      }
    }
  }

  /**
   * This method validates if the ColorOperator can be used with the color space
   * defined in OutputIntent dictionaries.
   *
   * @param operator
   *          the color operator
   * @throws ContentStreamException
   */
  protected void checkColorOperators(String operation)
      throws ContentStreamException {
    PDColorState cs = getColorState(operation);

    if ("rg".equals(operation) || "RG".equals(operation)) {
      if (!validColorSpace(cs, ColorSpaceType.RGB)) {
        throwContentStreamException("The operator \"" + operation
            + "\" can't be used with CMYK Profile",
            ERROR_GRAPHIC_INVALID_COLOR_SPACE_RGB);
      }
    }

    if ("k".equals(operation) || "K".equals(operation)) {
      if (!validColorSpace(cs, ColorSpaceType.CMYK)) {
        throwContentStreamException("The operator \"" + operation
            + "\" can't be used with RGB Profile",
            ERROR_GRAPHIC_INVALID_COLOR_SPACE_CMYK);
      }
    }

    if ("g".equals(operation) || "G".equals(operation)) {
      if (!validColorSpace(cs, ColorSpaceType.ALL)) {
        throwContentStreamException("The operator \"" + operation
            + "\" can't be used without Color Profile",
            ERROR_GRAPHIC_INVALID_COLOR_SPACE_MISSING);
      }
    }

    if ("f".equals(operation) || "F".equals(operation)
        || "f*".equals(operation) || "B".equals(operation)
        || "B*".equals(operation) || "b".equals(operation)
        || "b*".equals(operation)) {
      if (!validColorSpace(cs, ColorSpaceType.ALL)) {
        // ---- The default fill color needs an OutputIntent
        throwContentStreamException("The operator \"" + operation
            + "\" can't be used without Color Profile",
            ERROR_GRAPHIC_INVALID_COLOR_SPACE_MISSING);
      }
    }
  }

  private boolean validColorSpace(PDColorState colorState, ColorSpaceType expectedType) {
    boolean result = true;
    if (colorState == null) {
      result = validColorSpaceDestOutputProfile(expectedType);
    } else {
      PDColorSpace cs = colorState.getColorSpace();
      if (isDeviceIndependent(cs, expectedType)) {
        result = true;
      } else {
        result = validColorSpaceDestOutputProfile(expectedType);
      }
    }   
    return result;
  }

  /**
   * Check if the ColorProfile provided by the DestOutputProfile entry isn't null and
   * if the ColorSpace represented by the Profile has the right type (RGB or CMYK)
   *
   * @param expectedType
   * @return
   */
  private boolean validColorSpaceDestOutputProfile(ColorSpaceType expectedType) {
    boolean result = false;
    ICCProfileWrapper profileWrapper = documentHandler.getIccProfileWrapper();
    if (profileWrapper != null) {
      switch (expectedType) {
      case RGB:
        result = profileWrapper.isRGBColorSpace();
        break;
      case CMYK:
        result = profileWrapper.isCMYKColorSpace();
        break;
      default:
        result = true;
        break;
      }
    }
    return result;
  }
 
  /**
   * Return true if the given ColorSpace is an independent device ColorSpace.
   * If the color space is an ICCBased, check the embedded profile color (RGB or CMYK)
   * @param cs
   * @return
   */
  private boolean isDeviceIndependent(PDColorSpace cs, ColorSpaceType expectedType) {
    boolean result =  (cs instanceof PDCalGray || cs instanceof PDCalRGB || cs instanceof PDLab);
    if (cs instanceof PDICCBased) {
      PDICCBased iccBased = (PDICCBased)cs;
      try {
        ColorSpace iccColorSpace = iccBased.getJavaColorSpace();
        switch (expectedType) {
        case RGB:
          result = (iccColorSpace.getType() == ICC_ColorSpace.TYPE_RGB);
          break;
        case CMYK:
          result = (iccColorSpace.getType() == ICC_ColorSpace.TYPE_CMYK);
          break;
        default:
          result = true;
          break;
        }
      } catch (IOException e) {
        result = false;
      }     
    }
    return result;
  }

  /**
   * Return the current color state used by the operation
   * @param operation
   * @return
   */
  private PDColorState getColorState(String operation) {
    if (getGraphicsState() == null) {
      return null;
    }

    PDColorState colorState;
    if (operation.equals("rg") || operation.equals("g") || operation.equals("k")
        || operation.equals("f") || operation.equals("F") || operation.equals("f*")) {
      // non stroking operator
      colorState = getGraphicsState().getNonStrokingColor();
    } else {
      // stroking operator
      colorState = getGraphicsState().getStrokingColor();
    }
    return colorState;
  }

  /**
   * This method validates if the ColorSpace used as operand is consistent with
   * the color space defined in OutputIntent dictionaries.
   *
   * @param operator
   * @param arguments
   * @throws IOException
   */
  protected void checkSetColorSpaceOperators(PDFOperator operator,
      List<?> arguments) throws IOException {
    if (!("CS".equals(operator.getOperation()) || "cs".equals(operator
        .getOperation()))) {
      return;
    }

    String colorSpaceName = null;
    if (arguments.get(0) instanceof String) {
      colorSpaceName = (String) arguments.get(0);
    } else if (arguments.get(0) instanceof COSString) {
      colorSpaceName = ((COSString) arguments.get(0)).toString();
    } else if (arguments.get(0) instanceof COSName) {
      colorSpaceName = ((COSName) arguments.get(0)).getName();
    } else {
      throwContentStreamException("The operand doesn't have the expected type",
          ERROR_GRAPHIC_UNEXPECTED_VALUE_FOR_KEY);
    }

    ColorSpaceHelper csHelper = null;
    ColorSpaces cs = null;
    try {
      cs = ColorSpaces.valueOf(colorSpaceName);
    } catch (IllegalArgumentException e) {
      // ---- The color space is unknown.
      // ---- Try to access the resources dictionary, the color space can be a
      // reference.
      PDColorSpace pdCS = (PDColorSpace) this.getColorSpaces().get(
          colorSpaceName);
      if (pdCS != null) {
        cs = ColorSpaces.valueOf(pdCS.getName());
        csHelper = ColorSpaceHelperFactory.getColorSpaceHelper(pdCS,
            documentHandler, ColorSpaceRestriction.NO_RESTRICTION);
      }
    }

    if (cs == null) {
      throwContentStreamException("The ColorSpace is unknown",
          ERROR_GRAPHIC_UNEXPECTED_VALUE_FOR_KEY);
    }

    if (csHelper == null) {
      csHelper = ColorSpaceHelperFactory.getColorSpaceHelper(COSName
          .getPDFName(colorSpaceName), documentHandler,
          ColorSpaceRestriction.NO_RESTRICTION);
    }

    List<ValidationError> errors = new ArrayList<ValidationError>();
    try {
      if (!csHelper.validate(errors)) {
        ValidationError ve = errors.get(0);
        throwContentStreamException(ve.getDetails(), ve.getErrorCode());
      }
    } catch (ValidationException e) {
      //      throw new IOException(e.getMessage(), e); java 6
      throw new IOException(e.getMessage());
    }
  }

  /**
   * Build a ContentStreamException using the given parameters
   *
   * @param msg
   *          exception details
   * @param errorCode
   *          the error code.
   * @throws ContentStreamException
   */
  protected void throwContentStreamException(String msg, String errorCode)
      throws ContentStreamException {
    ContentStreamException cex = new ContentStreamException(msg);
    cex.setValidationError(errorCode);
    throw cex;
  }
}
TOP

Related Classes of org.apache.padaf.preflight.utils.ContentStreamEngine

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.