Custom stamps with custom images not loading correctly

1,119 views
Skip to first unread message

Iwan Luijks

unread,
Nov 11, 2014, 11:59:37 AM11/11/14
to pdfnet-w...@googlegroups.com
Hi all,

Using the example of the custom diamond annotation I tried to create a custom stamp with a sample image that can be inserted in the PDF-document you're currently editing.

I got it that far that when you click the stamp's icon in the toolbar it actually loads the sample image and displays it correctly, even saving the stamp works. However when I reload my page PDFtron shows the fallback 'draft'-stamp instead of my custom image and I cannot find a way to display my own image there.

The second thing I ran into was that when trying the print the PDF from Adobe Reader, it doesn't display the stamp in neither the print preview nor the actual printed document (having the settings to print both comments and stamps in Adobe Reader's print settings pane set.)

Could you provide me some insight in what I'm missing or what goes wrong here?

The code I have is:
<code>
/**
* ReaderControl config file
* ------------------------------
* This js file is meant to simplify configuring commonly used settings for ReaderControl.
* You can override default settings through ReaderControl.config properties, or add JavaScript code directly here.
*/
(function () {
"use strict";

/*
*@extends {Annotations.StampAnnotation}
*/
var CustomDiamondAnnotation = function () {
Annotations.StampAnnotation.call(this);
};
var CustomDiamondSelectionModel = function (annotation, canModify) {
Annotations.SelectionModel.call(this, annotation, canModify);

if (canModify) {
var controlHandles = this.getControlHandles();
controlHandles.push(new Annotations.BoxControlHandle(annotation, 'top'));
controlHandles.push(new Annotations.BoxControlHandle(annotation, 'left'));
controlHandles.push(new Annotations.BoxControlHandle(annotation, 'right'));
controlHandles.push(new Annotations.BoxControlHandle(annotation, 'bottom'));
}
};

CustomDiamondSelectionModel.prototype = $.extend(true, new Annotations.BoxSelectionModel(), CustomDiamondSelectionModel.prototype);

CustomDiamondAnnotation.prototype = $.extend(new Annotations.StampAnnotation(), {
elementName: "stamp",
/**
* Override the default draw method.
* Coordinate space is relative to the X,Y position, in the unmirrored quandrant:
* i.e. two mouse points used to create the annotations are (0,0) and (Width, Height)
* Mirroring is automatically applied if NoResize is false.
* @override
*/
draw: function (ctx) {
this.setStyles(ctx);
var img = $('<img src="data:image/png;base64,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"/>');

ctx.lineWidth = this.StrokeThickness;
ctx.rect(this.X, this.Y, 128, 128);
ctx.drawImage(img[0], this.X, this.Y, 128, 128);

this.SetHeight(128);
this.SetWidth(128);
},
selectionModel: CustomDiamondSelectionModel,
serialize: function (element, pageMatrix) {
var objElement = Annotations.StampAnnotation.prototype.serialize.call(this, element, pageMatrix);
return objElement;
},
deserialize: function (element, pageMatrix) {
Annotations.MarkupAnnotation.prototype.deserialize.call(this, element, pageMatrix);
console.log(element);
}
});

/**
* CustomDiamondCreateTool
* -for shape annotations based on two mouse points, extend from GenericAnnotationCreateTool/
*/
var CustomDiamondCreateTool = function (docViewer) {
//pass in the constructor to the custom Annotation
Tools.GenericAnnotationCreateTool.call(this, docViewer, CustomDiamondAnnotation);
};
CustomDiamondCreateTool.prototype = new Tools.GenericAnnotationCreateTool();
CustomDiamondCreateTool.prototype.mouseLeftUp = function (e) {
Tools.GenericAnnotationCreateTool.prototype.mouseLeftUp.call(this, e);
};

/**
* Override the default double click behavior for the AnnotationEditTool
*/
//Tools.AnnotationEditTool.prototype.mouseDoubleClick = function(e){
// Tools.AnnotationEditTool.prototype.mouseDoubleClick.call(this,e);
//}


//=========================================================
// Load a custom script for custom annotations and
// add a new tool button to the annotation panel
//=========================================================
$(document).bind("documentLoaded", function (event) {
// set annotation tab visible by default
readerControl.setVisibleTab(3);

// Add a new tool to the annotation panel
$("#toolModePicker").append('<li id="annot-button-custom-stamp" class="glyphicons image-icon" data-toolmode="AnnotationCreateCustomDiamond" title="Custom Diamond"><img src="/images_base/company/ing.jpg"/></li>');
$(window).resize();

//document finished loading
var objAnnotationManager = readerControl.getDocumentViewer().GetAnnotationManager();
//register Annotation for serialization
objAnnotationManager.registerAnnotationType("stamp", CustomDiamondAnnotation);
//register ToolMode for ReaderControl UI
readerControl.toolModeMap['AnnotationCreateCustomDiamond'] = CustomDiamondCreateTool;

});
})();
</code>

Matt Parizeau

unread,
Nov 12, 2014, 7:58:36 PM11/12/14
to pdfnet-w...@googlegroups.com
Hi Iwan,

Thanks for all the sample code! The problem is that you're setting the elementName property to "stamp" which is already an existing annotation type in WebViewer. So when you import your saved XFDF it will see an element of type "stamp" and then the default stamp annotation will deserialize it. The default stamp is Draft which is why you're seeing that.

If you set the elementName to "customstamp" (for example) and also call registerAnnotationType("customstamp", CustomDiamondAnnotation) then it should work.

As for printing the stamp from Adobe how did you get the annotation into it? Did you use PDFNet to import the XFDF back into the PDF? Unfortunately since you're using a custom annotation you'll only be able to use it within WebViewer because other programs (like Adobe) won't be able to import it.

Matt Parizeau
Software Developer
PDFTron Systems Inc.

Iwan Luijks

unread,
Nov 13, 2014, 4:25:31 AM11/13/14
to pdfnet-w...@googlegroups.com
Hi Matt,

The reason I set the elementName to 'stamp' is because of the fact that both XFDF and Adobe support this type of annotation out of the box and thus Adobe knows how to display it (apart from the image I draw onto the canvas context.)
When I change the elementName to 'customstamp' and register the annotation as 'customstamp' PDFTron doesn't save my annotation at all anymore.

The solution I am looking for is a way to let PDFTron, and Adobe Reader/Acrobat display the annotation, and make it printable too by at least Adobe Reader/Acrobat.
Might there be a way to add my image as an actual image to the PDF via the WebViewer API, which would completely resolve the annotation problem described here? Just like I would normally import an image into a PDF in Acrobat. I couldn't find anything about this in the docs (the reason I chose to try and create a custom annotation for accomplishing this.)


Kind regards,

Iwan Luijks

Matt Parizeau

unread,
Nov 13, 2014, 4:46:40 PM11/13/14
to pdfnet-w...@googlegroups.com
Hi Iwan,

To be able to have your custom image shown in Adobe you would probably have to manually use PDFNet's Stamper class to load and turn the image into a stamp. If you want to have generic stamps then one way might be to store the data URL as a custom attribute in XFDF. Then parse the XFDF using PDFNet and create a stamp in the same location as your annotation.


If you want to keep the annotation as an actual stamp annotation in WebViewer then you would have to override the Annotations.StampAnnotation class. You could keep the custom tool but in mouseLeftDown you could set a property on the annotation to mark it as a custom stamp (similar to this example https://groups.google.com/d/msg/pdfnet-webviewer/HTIWeucA4AU/SdRnAwMy4gsJ). Then in the draw function you could check the custom attribute and draw the custom image in that case, otherwise call the default stamp drawing function.

For example:
var stampDraw = Annotations.StampAnnotation.prototype.draw;
Annotations.StampAnnotation.prototype.draw = function(ctx) {
   
if (this.customImage) {
       
// draw custom image
   
} else {
        stampDraw
.apply(this, arguments);
   
}
};


Matt Parizeau
Software Developer
PDFTron Systems Inc.

Iwan Luijks

unread,
Nov 19, 2014, 11:48:42 AM11/19/14
to pdfnet-w...@googlegroups.com
Hi Matt,

It took some while, but I was able to completely figure it out and have the custom annotations printed, shown in the WebViewer and redrawn in the WebViewer. What I did was the following:
- In PHP I create a temporary PDF for editing (this is important for loading the PDF, see a few steps below.)
- Created an annotation that basically extends the StampAnnotation, uses the BoxSelectionModel, and for creating the actual annotation the GenericAnnotationCreateTool is used. While drawing and serializing the annotation I set the 'Subject' attribute with a custom identifier for that type of Stamp (e.g. CustomSignHere).
This allows me to draw the new annotation on the screen (as shown in my previous code example, I actually draw an image on the Stamp's canvas.)
- On save I create images from the actual stamps, based on the 'Subject' every custom annotation is given I know which stamp to replace with which image (Stamp with Subject 'CustomSignHere' is replaced with 'customsignhere.png', 'CustomSignThere' with 'customsignthere.png', etc..). For this step I used the ElementBuilder and ElementWriter to allow for more advanced things than the Stamper.
Since it is now an image, still including the Subject, placed within the PDF, we can print it from all PDF-viewers.
- When the document is loading from the WebViewer while it contains the custom stamps, I remove the images having a Subject (that was included within the PDF as mentioned in the step above) indicating the annotation is a custom one. Since I created a temporary PDF first, and that PDF is the one loaded into the WebViewer, when the page is reloaded I won't lose my custom annotations and only the temporary PDF is disposed at that point.
As said, the temporary PDF allows me to keep my custom annotations when the page is reloaded, either by accident or something else.
- After the document is loaded into the WebViewer, because I override the standard Stamp's draw function, I can now draw my image back on top of the Stamp's canvas again to have it shown in the WebViewer as if it was drawn just at that moment by the user:
       var originalStampDrawFunction = Annotations.StampAnnotation.prototype.draw;

       
Annotations.StampAnnotation.prototype.draw = function (ctx) {
           
switch (this.Subject) {
               
case 'CustomSignHere':
                   
CustomSignHere.prototype.draw.call(this, ctx);
                   
break;
               
case 'CustomSignThere':
                   
CustomSignThere.prototype.draw.call(this, ctx);
                   
break;
               
default:
                    originalStampDrawFunction
.apply(this, arguments);
           
}
       
};


This solution, gives me printable, fully customizable, annotations, shown in all PDF viewers, and easily found within the PDF's binary source (because of the custom Subject.)


Kind regards,

Iwan Luijks



Op donderdag 13 november 2014 22:46:40 UTC+1 schreef Matt Parizeau:

Matt Parizeau

unread,
Nov 19, 2014, 7:25:07 PM11/19/14
to pdfnet-w...@googlegroups.com
Hi Iwan,

I'm glad you've gotten it working! I'm not sure how you're saving annotations (from the save button?) but it seems like you've hooked it up to merge the annotations back to the PDF and so when the page is reloaded you'll be loading that PDF with the merged in annotations? If you're curious about why it's working I'm not entirely clear on your setup (seems like converting to XOD on the fly?) but if you're happy with it then that's great!

Matt Parizeau
Software Developer
PDFTron Systems Inc.

Reply all
Reply to author
Forward
0 new messages