•  
  •  
  •  
 

Burning annotations WITHOUT the WebAnnotationViewer

Last post 15 Jan 2010, 5:16 PM by smaglio81. 4 replies.
Sort Posts: Previous Next
  •  15 Jan 2010, 12:11 AM 20038

    Burning annotations WITHOUT the WebAnnotationViewer

    Lou Franco did a nice write-up on how to burn annotations to an image without the usage of a "viewer" (http://www.atalasoft.com/cs/forums/thread/18380.aspx). But, I am unable to get the annotations I want to load into a LayerCollection object. Mostly because I am having a problem figuring out how to use the AnnoationDataImporters (I'm not even sure I'm supposed to use them).

    I have replaced this section of Lou Franco's code:

    // create annotations
    LayerCollection coll = new LayerCollection();
    LayerAnnotation l = new LayerAnnotation();
    Coll.add(l);
    TextAnnotation t = new TextAnnotation("hello");
    t.Data.Location = new PointF(10f, 10f);
    t.Data.Size = new SizeF(50f, 50f);
    ((TextData)t.Data).Fill = new AnnotationBrush(Color.Yellow);
    l.Items.Add(t);


    With some code which loads Xmp data from an in memory object. The Xmp data is saved from a AnnotationViewer and is stored to a database as xml. The in memory Xmp/xml data, annotations.Data, is from the database. The code is:

    using Atalasoft.Annotate.Importers;

    ...

    var layerCollection = new LayerCollection();
    using( var annoStream = new MemoryStream( annotations.Data ) )
    {
        var importer = new XmpAnnotationDataImporter( annoStream );
        importer.Load();     // forcing the error to occur

        for( int i = 0; i < importer.PageCount; i++ )
        {
            layerCollection.Add( new LayerAnnotation( importer.Import( i ) ) );
        }
    }


    I am getting an error of "Xmp data stream is neither JPEG nor TIFF." Which makes me think that I don't have the appropriate decoder loaded??

    I do have a PdfDecoder registered. And, if I try to use PdfAnnotationDataImporter in place of XmpAnnotationDataImporter, then I get the error message "Invalid character in hex string: ? at offset 2 – please contact Atalasoft Support". (I'm pretty sure that error occurs because it's Xmp data.)

    As a side note, I can load "annotations.Data" into a WebAnnotationViewer with LoadAnnotationData( Stream ). But, I'm trying to burn the image without the usage of an AnnotationViewer. So, I'm looking for a way to correctly load the annotations into a LayerCollection or AnnotationDataCollection.

  •  15 Jan 2010, 8:42 AM 20042 in reply to 20038

    Re: Burning annotations WITHOUT the WebAnnotationViewer

    The AnnotationDataImporter classes are used to extract annotation data from files, such as JPEG, TIFF or PDF.  Since you already have XMP data, you would use the XmpFormatter class instead. 

    XmpFormatter.Deserialize will convert the XMP data into annotation objects. What is returned may be different depending on what the XMP data contains:  It may be an array of LayerData, a single LayerData object or an AnnotationData object.

  •  15 Jan 2010, 1:33 PM 20046 in reply to 20042

    Re: Burning annotations WITHOUT the WebAnnotationViewer

    Thanks, so much!

    I guess the Xmp data we're using came back as a LayerData[]. Is there a property which can be checked to determine what was returned? Or, do you need to use a if( obj is type ) statement?

    Also, the LayerData[] contains AnnotationData items (I guess that is to be expected). But, the BurnedFileImageSource class was expecting a LayerCollection filled with LayerAnnotation/AnnotationUI objects. Fortunately, the Xmp data I was using for testing only contained TextData objects. So, converting the Data objects to AnnotationUI objects was simplified.

    Is there a library which will automatically convert a LayerData object into a LayerAnnotation object? Or convert an individual Data object into an AnnotationUI object?


    The quick code used for testing:

    var layerCollection   = new LayerCollection();
    using( var annoStream = new MemoryStream( annotations.Data ) )
    {
        var formatter  = new XmpFormatter();
        var xmpData    = (LayerData[]) formatter.Deserialize( annoStream );

        for( int i = 0; i < xmpData.GetLength( 0 ); i++ )
        {
            var layerAnnotation  = new LayerAnnotation();
            foreach( var annoData in xmpData[i].Items )
            {
                var ui = new TextAnnotation( (TextData) annoData );
                layerAnnotation.Items.Add( ui );
            }
            layerCollection.Add( layerAnnotation );
        }
    }


  •  15 Jan 2010, 3:01 PM 20048 in reply to 20046

    Re: Burning annotations WITHOUT the WebAnnotationViewer

    You will need to test the result to find out if it's LayerData[], LayerData or AnnotationData.  Most of the time it will by LayerData[] since annotations are usually saved from a LayerCollection.

    You can convert the LayerData into a LayerAnnotation with the following code:

            private static AnnotationUIFactoryCollection _factories = new AnnotationUIFactoryCollection();
            private static LayerAnnotation LayerDataToUI(LayerData data)
            {
                LayerAnnotation layer = new LayerAnnotation(data);
                layer.CreateAnnotationUIObjects(_factories);
                return layer;
            }

    This will convert the LayerData and all of its child AnnotationData objects into AnnotationUI objects.  If you have any custom annotations, you will need to add their UI factories to the AnnotationUIFactoryCollection for this to work.

  •  15 Jan 2010, 5:16 PM 20049 in reply to 20048

    Re: Burning annotations WITHOUT the WebAnnotationViewer

    Thanks again! That works great.

    By the way, I updated Lou Franco's BurnedFileImageSource to also work with image files as a byte[] or Stream (attached).

    This updated code can handle burning an image in memory (for generating a document from a database source):

    protected static AnnotationUIFactoryCollection _factories = new AnnotationUIFactoryCollection();

    protected Stream BurnAnnotationsToImage( byte[] images, byte[] annotations )
    {
        if( annotations == null )
        {
            return new MemoryStream(images);
        }

        RegisteredDecoders.Decoders.Add( new PdfDecoder() );

        var layerCollection   = new LayerCollection();
        using( var annoStream = new MemoryStream( annotations ) )
        {
            var formatter  = new XmpFormatter();
            var xmpData    = (LayerData[]) formatter.Deserialize( annoStream );

            foreach( var annoData in xmpData )
            {
                var layerAnno    = new LayerAnnotation( annoData );
                layerAnno.CreateAnnotationUIObjects( _factories );
                layerCollection.Add( layerAnno );
            }
        }

        var imgSource = new BurnedFileImageSource( images, layerCollection );
        var asPdf     = new PdfEncoder();

        var stream    = new MemoryStream();
        while (imgSource.HasMoreImages())
        {
            var img = imgSource.AcquireNext();
            img.Save( stream, asPdf, null);
        }

        stream.Seek( 0, SeekOrigin.Begin );
        return stream;
    }


View as RSS news feed in XML