ContentScanner.cs :  » PDF » PDF-Clown » it » stefanochizzolini » clown » documents » contents » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » PDF » PDF Clown 
PDF Clown » it » stefanochizzolini » clown » documents » contents » ContentScanner.cs
/*
  Copyright 2007,2008 Stefano Chizzolini. http://clown.stefanochizzolini.it

  Contributors:
    * Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it)

  This file should be part of the source code distribution of "PDF Clown library"
  (the Program): see the accompanying README files for more info.

  This Program is free software; you can redistribute it and/or modify it under
  the terms of the GNU General Public License as published by the Free Software
  Foundation; either version 2 of the License, or (at your option) any later version.

  This Program is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY, either expressed or implied; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.

  You should have received a copy of the GNU General Public License along with this
  Program (see README files); if not, go to the GNU website (http://www.gnu.org/).

  Redistribution and use, with or without modification, are permitted provided that such
  redistributions retain the above copyright notice, license and disclaimer, along with
  this list of conditions.
*/

using it.stefanochizzolini.clown.bytes;
using colorsit.stefanochizzolini.clown.documents.contents.colorSpaces;
using fontsit.stefanochizzolini.clown.documents.contents.fonts;
using it.stefanochizzolini.clown.documents.contents.objects;
using it.stefanochizzolini.clown.files;
using it.stefanochizzolini.clown.objects;

using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;

namespace it.stefanochizzolini.clown.documents.contents{
  /**
    <summary>Content objects scanner.</summary>
    <remarks>
      <para>It wraps the content objects collection (Contents) to scan its graphics state
      through a forward cursor.</para>
      <para>Scanning is performed at an arbitrary deepness, according to the content objects nesting:
      in fact, each deepness level corresponds to a scan level so that at any time it's possible to seamlessly navigate across the levels (see ParentLevel, ChildLevel, LeafLevel).</para>
    </remarks>
  */
  public sealed class ContentScanner
  {
    #region types
    /**
      <summary>Graphics state [PDF:1.6:4.3].</summary>
    */
    public sealed class GraphicsState
      : ICloneable
    {
      #region static
      #region interface
      #region public
      /**
        <summary>Concatenates the given coordinate matrices.</summary>
      */
      public static double[] Concat(
        double[] matrix1,
        double[] matrix2
        )
      {
        double[] result = new double[6];
        result[0] = matrix1[0]*matrix2[0] + matrix1[1]*matrix2[2]; // a.
        result[1] = matrix1[0]*matrix2[1] + matrix1[1]*matrix2[3]; // b.
        result[2] = matrix1[2]*matrix2[0] + matrix1[3]*matrix2[2]; // c.
        result[3] = matrix1[2]*matrix2[1] + matrix1[3]*matrix2[3]; // d.
        result[4] = matrix1[4]*matrix2[0] + matrix1[5]*matrix2[2] + 1*matrix2[4]; // e.
        result[5] = matrix1[4]*matrix2[1] + matrix1[5]*matrix2[3] + 1*matrix2[5]; // f.

        return result;
      }
      #endregion
      #endregion
      #endregion

      #region dynamic
      #region fields
      /* NOTE: Initialized using prescribed default values. */
      internal double charSpace = 0;
      internal double[] ctm = new double[]{1,0,0,1,0,0};
      internal colors::Color fillColor = colors::DeviceGrayColor.Default;
      internal colors::ColorSpace fillColorSpace = colors::DeviceGrayColorSpace.Default;
      internal fonts::Font font = null;
      internal double fontSize = 0;
      internal double lead = 0;
      internal LineCapEnum lineCap = LineCapEnum.Butt;
      internal LineDash lineDash = new LineDash();
      internal LineJoinEnum lineJoin = LineJoinEnum.Miter;
      internal double lineWidth = 1;
      internal double miterLimit = 10;
      internal TextRenderModeEnum renderMode = TextRenderModeEnum.Fill;
      internal double rise = 0;
      internal double scale = 100;
      internal colors::Color strokeColor = colors::DeviceGrayColor.Default;
      internal colors::ColorSpace strokeColorSpace = colors::DeviceGrayColorSpace.Default;
      internal double[] tm = new double[]{1,0,0,1,0,0};
      internal double wordSpace = 0;

      private ContentScanner scanner;
      #endregion

      #region constructors
      internal GraphicsState(ContentScanner scanner)
      {this.scanner = scanner;}
      #endregion

      #region interface
      #region public
      /**
        <summary>Gets a deep copy of the graphics state object.</summary>
      */
      public object Clone(
        )
      {
        // Shallow copy.
        GraphicsState clone = (GraphicsState)this.MemberwiseClone();

        // Deep copy.
        /* NOTE: Mutable objects are to be cloned. */

        return clone;
      }

      /**
        <summary>Gets the current character spacing [PDF:1.6:5.2.1].</summary>
      */
      public double CharSpace
      {get{return charSpace;}}

      /**
        <summary>Gets the current transformation matrix.</summary>
      */
      public double[] CTM
      {get{return ctm;}}

      /**
        <summary>Gets the current color for nonstroking operations [PDF:1.6:4.5.1].</summary>
      */
      public colors::Color FillColor
      {get{return fillColor;}}

      /**
        <summary>Gets the current color space for nonstroking operations [PDF:1.6:4.5.1].</summary>
      */
      public colors::ColorSpace FillColorSpace
      {get{return fillColorSpace;}}

      /**
        <summary>Gets the current font.</summary>
      */
      public fonts::Font Font
      {get{return font;}}

      /**
        <summary>Gets the current font size.</summary>
      */
      public double FontSize
      {get{return fontSize;}}

      /**
        <summary>Gets the current leading [PDF:1.6:5.2.4].</summary>
      */
      public double Lead
      {get{return lead;}}

      /**
        <summary>Gets the current line cap style [PDF:1.6:4.3.2].</summary>
      */
      public LineCapEnum LineCap
      {get{return lineCap;}}

      /**
        <summary>Gets the current line dash pattern [PDF:1.6:4.3.2].</summary>
      */
      public LineDash LineDash
      {get{return lineDash;}}

      /**
        <summary>Gets the current line join style [PDF:1.6:4.3.2].</summary>
      */
      public LineJoinEnum LineJoin
      {get{return lineJoin;}}

      /**
        <summary>Gets the current line width [PDF:1.6:4.3.2].</summary>
      */
      public double LineWidth
      {get{return lineWidth;}}

      /**
        <summary>Gets the current miter limit [PDF:1.6:4.3.2].</summary>
      */
      public double MiterLimit
      {get{return miterLimit;}}

      /**
        <summary>Gets the current text rendering mode [PDF:1.6:5.2.5].</summary>
      */
      public TextRenderModeEnum RenderMode
      {get{return renderMode;}}

      /**
        <summary>Gets the current text rise [PDF:1.6:5.2.6].</summary>
      */
      public double Rise
      {get{return rise;}}

      /**
        <summary>Gets the current horizontal scaling [PDF:1.6:5.2.3].</summary>
      */
      public double Scale
      {get{return scale;}}

      /**
        <summary>Gets the current color for stroking operations [PDF:1.6:4.5.1].</summary>
      */
      public colors::Color StrokeColor
      {get{return strokeColor;}}

      /**
        <summary>Gets the current color space for stroking operations [PDF:1.6:4.5.1].</summary>
      */
      public colors::ColorSpace StrokeColorSpace
      {get{return strokeColorSpace;}}

      /**
        <summary>Resolves the given text-space point to its equivalent device-space one [PDF:1.6:5.3.3].</summary>
      */
      public PointF TextToDeviceSpace(
        PointF point
        )
      {
      //TODO:verify whether text parameters are relevant in this context!!!
        /*
          NOTE: The text rendering matrix (trm) is obtained from the concatenation
          of the text parameters matrix, the text matrix (tm) and the CTM.
        */
        double[] trm = Concat(tm,ctm);

        return new PointF(
          (float)(trm[0] * point.X + trm[2] * point.Y + trm[4]),
          (float)(((IPdfNumber)scanner.ContentContext.Box[3]).RawValue - (trm[1] * point.X + trm[3] * point.Y + trm[5]))
          );
      }

      /**
        <summary>Gets the text matrix.</summary>
      */
      public double[] TM
      {get{return tm;}}

      /**
        <summary>Resolves the given user-space point to its equivalent device-space one [PDF:1.6:4.2.3].</summary>
      */
      public PointF UserToDeviceSpace(
        PointF point
        )
      {
        return new PointF(
          (float)(ctm[0] * point.X + ctm[2] * point.Y + ctm[4]),
          (float)(ctm[1] * point.X + ctm[3] * point.Y + ctm[5])
          );
      }

      /**
        <summary>Gets the current word spacing [PDF:1.6:5.2.2].</summary>
      */
      public double WordSpace
      {get{return wordSpace;}}
      #endregion
      #endregion
      #endregion
    }
    #endregion

    #region dynamic
    #region fields
    /**
      Content objects collection.
    */
    private Contents contents;

    /**
      Current object index at this level.
    */
    private int index = 0;

    /**
      Current object collection at this level.
    */
    /*
      NOTE: This wrapping class represents an ugly workaround to the lack of support to covariance
      within C#. TODO: it should be temporary, to eliminate before the stable release.
      Ideally, in the ContentScanner constructors the objects collection should have been assigned
      directly for any type derived from ContentObject (see Java generics wildcards), this way:
      this.objects = (List<? : ContentObject>)((CompositeObject<? : ContentObject>)parentLevel.Current).Objects;
      Using ConvertAll method was not an option, because keeping a reference to the original collection
      was necessary.
    */
    private class GenericObjects
    {
      private object objects;

      internal GenericObjects(
        object objects
        )
      {this.objects = objects;}

      internal GenericObjects(
        ContentObject parent
        )
      {
        if(parent is CompositeObject<Operation>)
        {this.objects = ((CompositeObject<Operation>)parent).Objects;}
        else
        {this.objects = ((CompositeObject<ContentObject>)parent).Objects;}
      }

      public int Count
      {
        get
        {
          if(objects is IList<Operation>)
            return ((IList<Operation>)objects).Count;
          else
            return ((IList<ContentObject>)objects).Count;
        }
      }

      public ContentObject this[
        int index
        ]
      {
        get
        {
          if(objects is IList<Operation>)
            return ((IList<Operation>)objects)[index];
          else
            return ((IList<ContentObject>)objects)[index];
        }
        set
        {
          if(objects is IList<Operation>)
          {((IList<Operation>)objects)[index] = (Operation)value;}
          else
          {((IList<ContentObject>)objects)[index] = value;}
        }
      }

      public void Insert(
        int index,
        ContentObject obj
        )
      {
        if(objects is IList<Operation>)
        {((IList<Operation>)objects).Insert(index,(Operation)obj);}
        else
        {((IList<ContentObject>)objects).Insert(index,obj);}
      }

      public void RemoveAt(
        int index
        )
      {
        if(objects is IList<Operation>)
        {((IList<Operation>)objects).RemoveAt(index);}
        else
        {((IList<ContentObject>)objects).RemoveAt(index);}
      }
    }
    private GenericObjects objects;

    /**
      Current graphics state.
    */
    private GraphicsState state;

    /**
      Child level.
    */
    private ContentScanner childLevel;
    /**
      Parent level.
    */
    private ContentScanner parentLevel;
    /**
      Operation level.
    */
    private ContentScanner leafLevel;
    #endregion

    #region constructors
    /**
      <param name="contents">Content objects collection to scan.</param>
    */
    public ContentScanner(
      Contents contents
      )
    {
      this.contents = contents;
      this.objects = new GenericObjects(this.contents);
      this.state = new GraphicsState(this);

      Refresh();
    }

    /**
      <param name="parentLevel">Parent scan level.</param>
    */
    private ContentScanner(
      ContentScanner parentLevel
      )
    {
      this.parentLevel = parentLevel;
      this.contents = parentLevel.contents;
      this.objects = new GenericObjects(parentLevel.Current);
      this.state = (GraphicsState)parentLevel.state.Clone();

      Refresh();
    }
    #endregion

    #region interface
    #region public
    /**
      <summary>Gets the current child object.</summary>
    */
    public ContentObject Child
    {get{return childLevel.Current;}}

    /**
      <summary>Gets the child scan level.</summary>
    */
    public ContentScanner ChildLevel
    {get{return childLevel;}}

    /**
      <summary>Gets the content context associated to the content objects collection.</summary>
    */
    public IContentContext ContentContext
    {get{return contents.ContentContext;}}

    /**
      <summary>Gets the contents collection this scanner is inspecting.</summary>
    */
    public Contents Contents
    {get{return contents;}}

    /**
      <summary>Gets/Sets the current content object.</summary>
    */
    public ContentObject Current
    {
      get
      {
        try{return (ContentObject)objects[index];}
        catch{return null;}
      }
      set
      {
        objects[index] = value;

        Refresh();
      }
    }

    /**
      <summary>Gets the current position.</summary>
    */
    public int Index
    {get{return index;}}

    /**
      <summary>Inserts a content object at the current position.</summary>
    */
    public void Insert(
      ContentObject obj
      )
    {
      objects.Insert(index,obj);

      Refresh();
    }

    /**
      <summary>Inserts content objects at the current position.</summary>
      <remarks>After insertion complete, lastly-inserted content object is at the current
      position.</remarks>
    */
    public void Insert<T>(
      ICollection<T> objects
      ) where T : ContentObject
    {
      int index = 0;
      int count = objects.Count;
      foreach(ContentObject obj in objects)
      {
        Insert(obj);

        if(++index < count)
        {MoveNext();}
      }
    }

    /**
      <summary>Gets the current leaf object.</summary>
    */
    public ContentObject Leaf
    {get{return leafLevel.Current;}}

    /**
      <summary>Gets the leaf scan level.</summary>
    */
    public ContentScanner LeafLevel
    {get{return leafLevel;}}

    /**
      <summary>Moves to the first object.</summary>
      <returns>Whether the first object was successfully reached.</returns>
    */
    public bool MoveFirst(
      )
    {
      index = 0;
      if(parentLevel == null)
      {this.state = new GraphicsState(this);}
      else
      {this.state = (GraphicsState)parentLevel.state.Clone();}

      Refresh();

      return (Current != null);
    }

    /**
      <summary>Moves to the last object.</summary>
      <returns>Whether the last object was successfully reached.</returns>
    */
    public bool MoveLast(
      )
    {
      int lastIndex = contents.Count-1;
      bool success = (index == lastIndex);
      while(index < lastIndex)
      {success = MoveNext();}

      return success;
    }

    /**
      <summary>Moves to the next object.</summary>
      <returns>Whether the next object was successfully reached.</returns>
    */
    public bool MoveNext(
      )
    {
      // Updating the current graphics state...
      {
        ContentObject currentObject = Current;
        if(currentObject is Operation)
        {
          Operation currentOperation = (Operation)currentObject;
          if(currentOperation is ModifyCTM)
          {state.ctm = ((ModifyCTM)currentOperation).ApplyTo(state.CTM);}
          else if(currentOperation is SetCharSpace)
          {state.charSpace = ((SetCharSpace)currentOperation).Value;}
          else if(currentOperation is SetLineCap)
          {state.lineCap = ((SetLineCap)currentOperation).Value;}
          else if(currentOperation is SetLineDash)
          {state.lineDash = ((SetLineDash)currentOperation).Value;}
          else if(currentOperation is SetLineJoin)
          {state.lineJoin = ((SetLineJoin)currentOperation).Value;}
          else if(currentOperation is SetLineWidth)
          {state.lineWidth = ((SetLineWidth)currentOperation).Value;}
          else if(currentOperation is SetMiterLimit)
          {state.miterLimit = ((SetMiterLimit)currentOperation).Value;}
          else if(currentOperation is SetTextLead)
          {state.lead = ((SetTextLead)currentOperation).Value;}
          else if(currentOperation is SetTextRise)
          {state.rise = ((SetTextRise)currentOperation).Value;}
          else if(currentOperation is SetTextScale)
          {state.scale = ((SetTextScale)currentOperation).Value;}
          else if(currentOperation is SetTextMatrix)
          {state.tm = ((SetTextMatrix)currentOperation).GetMatrix();}
          else if(currentOperation is SetTextRenderMode)
          {state.renderMode = ((SetTextRenderMode)currentOperation).Value;}
          else if(currentOperation is SetWordSpace)
          {state.wordSpace = ((SetWordSpace)currentOperation).Value;}
          else if(currentOperation is SetFont)
          {
            SetFont setFontOperation = (SetFont)currentOperation;
            state.font = ContentContext.Resources.Fonts[setFontOperation.Name];
            state.fontSize = setFontOperation.Size;
          }
          else if(currentOperation is SetStrokeColor)
          {
            state.strokeColor = state.strokeColorSpace.GetColor(
              currentOperation.Operands.ToArray()
              );
          }
          else if(currentOperation is SetFillColor)
          {
            state.fillColor = state.fillColorSpace.GetColor(
              currentOperation.Operands.ToArray()
              );
          }
          else if(currentOperation is SetStrokeColorSpace)
          {
            /*
              NOTE: The names DeviceGray, DeviceRGB, DeviceCMYK, and Pattern always identify
              the corresponding color spaces directly; they never refer to resources in the
              ColorSpace subdictionary [PDF:1.6:4.5.7].
            */
            PdfName name = (PdfName)currentOperation.Operands[0];
            if(name.Equals(PdfName.DeviceGray))
            {state.strokeColorSpace = colors::DeviceGrayColorSpace.Default;}
            else if(name.Equals(PdfName.DeviceRGB))
            {state.strokeColorSpace = colors::DeviceRGBColorSpace.Default;}
            else if(name.Equals(PdfName.DeviceCMYK))
            {state.strokeColorSpace = colors::DeviceCMYKColorSpace.Default;}
      //TODO:special color spaces[PDF:1.6:4.5.5]!!!
      //       else if(name.equals(PdfName.Pattern))
      //       {state.strokeColorSpace = Pattern.Default;}
            else
            {state.strokeColorSpace = ContentContext.Resources.ColorSpaces[name];}
//TODO:eliminate when full support to color spaces!!!
if(state.strokeColorSpace != null)
{
            /*
              NOTE: The operation also sets the current stroking color
              to its initial value, which depends on the color space [PDF:1.6:4.5.7].
            */
            state.strokeColor = state.strokeColorSpace.GetDefaultColor();
}
          }
          else if(currentOperation is SetFillColorSpace)
          {
            /*
              NOTE: The names DeviceGray, DeviceRGB, DeviceCMYK, and Pattern always identify
              the corresponding color spaces directly; they never refer to resources in the
              ColorSpace subdictionary [PDF:1.6:4.5.7].
            */
            PdfName name = (PdfName)currentOperation.Operands[0];
            if(name.Equals(PdfName.DeviceGray))
            {state.fillColorSpace = colors::DeviceGrayColorSpace.Default;}
            else if(name.Equals(PdfName.DeviceRGB))
            {state.fillColorSpace = colors::DeviceRGBColorSpace.Default;}
            else if(name.Equals(PdfName.DeviceCMYK))
            {state.fillColorSpace = colors::DeviceCMYKColorSpace.Default;}
      //TODO:special color spaces[PDF:1.6:4.5.5]!!!
      //       else if(name.equals(PdfName.Pattern))
      //       {state.fillColorSpace = Pattern.Default;}
            else
            {state.fillColorSpace = ContentContext.Resources.ColorSpaces[name];}
//TODO:eliminate when full support to color spaces!!!
if(state.fillColorSpace != null)
{
            /*
              NOTE: The operation also sets the current nonstroking color
              to its initial value, which depends on the color space [PDF:1.6:4.5.7].
            */
            state.fillColor = state.fillColorSpace.GetDefaultColor();
}
          }
        }
      }

      // Trying to move to the next object...
      if(index < objects.Count)
      {index++;}

      Refresh();

      return (Current != null);
    }

    /**
      <summary>Gets the current parent object.</summary>
    */
    public CompositeObject<ContentObject> Parent
    {get{return (CompositeObject<ContentObject>)parentLevel.Current;}}

    /**
      <summary>Gets the parent scan level.</summary>
    */
    public ContentScanner ParentLevel
    {get{return parentLevel;}}

    /**
      <summary>Removes the content object at the current position.</summary>
      <returns>Removed object.</returns>
    */
    public ContentObject Remove(
      )
    {
      ContentObject oldObject = Current;
      objects.RemoveAt(index);

      Refresh();

      return oldObject;
    }

    /**
      <summary>Gets the current graphics state applied to the current content object.</summary>
      <remarks>The returned object of this method is fundamental for any content manipulation
      as it represents the actual constraints that affect the current content object
      rendering.</remarks>
    */
    public GraphicsState State
    {get{return state;}}
    #endregion

    #region private
    /**
      <summary>Synchronizes the scanner state.</summary>
    */
    private void Refresh(
      )
    {
      if(Current is CompositeObject)
      {
        childLevel = new ContentScanner(this);
        leafLevel = childLevel.LeafLevel;
      }
      else
      {
        childLevel = null;
        leafLevel = this;
      }
    }
    #endregion
    #endregion
    #endregion
  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.