Display custom annotations in other PDF viewers after downloading

207 views
Skip to first unread message

Matt Parizeau

unread,
Feb 6, 2019, 2:49:48 PM2/6/19
to PDFTron WebViewer
Q:

I've created a custom annotation following the guide here https://www.pdftron.com/documentation/web/guides/annotations/custom-annotations and when I download the PDF it isn't visible in other PDF viewers. Is it possible to make these annotations appear?

A:

One approach you could take is to draw the annotation as an image and export it as a stamp annotation which will be visible in any PDF viewer. When you export the annotation you can also prepend some information the annotation's ID to identify that the stamp is one of your custom annotations. Then when you reopen the saved file in your viewer you can check for that information in the annotation's ID and create your custom annotation instead. 

Below is some sample code that does this with a custom triangle annotation:

var exportAnnotations = CoreControls.AnnotationManager.prototype.exportAnnotations;
CoreControls.AnnotationManager.prototype.exportAnnotations = function() {
  // change custom annotations into stamp annotations when exporting XFDF
  var originalElementName = TriangleAnnotation.prototype.elementName;
  TriangleAnnotation.prototype.elementName = 'stamp';

  var data = exportAnnotations.apply(this, arguments);
  TriangleAnnotation.prototype.elementName = originalElementName;
  return data;
};

var stampDeserialize = Annotations.StampAnnotation.prototype.deserialize;
Annotations.StampAnnotation.prototype.deserialize = function(element) {
  stampDeserialize.apply(this, arguments);
  // if we find a stamp with a custom id then it should be a custom annotation
  if (this.Id.indexOf('custom-') === 0) {
    // create a new custom annotation
    var pageMatrix = readerControl.docViewer.getDocument().getPageMatrix(this.PageNumber - 1);
    var triangle = new TriangleAnnotation();
    triangle.deserialize(element, pageMatrix);

    var stamp = this;
    setTimeout(function() {
      // remove the old stamp we deserialized and replace with the new custom annotation type
      var annotManager = readerControl.docViewer.getAnnotationManager();
      annotManager.addAnnotation(triangle);
      annotManager.deleteAnnotation(stamp);
      annotManager.drawAnnotations(triangle.PageNumber);
    }, 0);
  }
};


var exportCanvas = document.createElement('canvas');

var TriangleAnnotation = function() {
  Annotations.MarkupAnnotation.call(this);
  this.Subject = 'Triangle';
  // so that we can determine it's a custom annotation when loading back from a saved file
  this.Id = 'custom-' + this.Id;
};

TriangleAnnotation.prototype = $.extend(new Annotations.MarkupAnnotation(), {
  elementName: 'triangle',

  draw: function(ctx, pageMatrix) {
    this.setStyles(ctx, pageMatrix);

    ctx.translate(this.X, this.Y);
    ctx.beginPath();
    ctx.moveTo(this.Width / 2, 0);
    ctx.lineTo(this.Width, this.Height);
    ctx.lineTo(0, this.Height);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
  },

  generateImage: function() {
    // draw annotation onto offscreen canvas to get the data URL for export
    exportCanvas.width = this.Width;
    exportCanvas.height = this.Height;
    var ctx = exportCanvas.getContext('2d');
    ctx.translate(-this.X, -this.Y);

    var pageMatrix = readerControl.docViewer.getDocument().getPageMatrix(this.PageNumber - 1);
    this.draw(ctx, pageMatrix);

    return exportCanvas.toDataURL();
  },

  serialize: function(element) {
    // serialize image data so that appearance for the stamp will be visible in other viewers
    element = Annotations.MarkupAnnotation.prototype.serialize.apply(this, arguments);
    var imageData = document.createElementNS('', 'imagedata');
    imageData.textContent = this.generateImage().replace(new RegExp('%0A', 'g'), '');
    element.appendChild(imageData);

    return element;
  }
});


var TriangleCreateTool = function(docViewer) {
  Tools.GenericAnnotationCreateTool.call(this, docViewer, TriangleAnnotation);
};
TriangleCreateTool.prototype = new Tools.GenericAnnotationCreateTool();



var triangleToolName = 'AnnotationCreateTriangle';

$(document).on('viewerLoaded', function() {
  var am = readerControl.docViewer.getAnnotationManager();
  // register the annotation type so that it can be saved to XFDF files
  am.registerAnnotationType(TriangleAnnotation.prototype.elementName, TriangleAnnotation);

  var triangleTool = new TriangleCreateTool(readerControl.docViewer);
  readerControl.registerTool({
    toolName: triangleToolName,
    toolObject: triangleTool,
    buttonImage: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">' +
      '<path d="M12 7.77L18.39 18H5.61L12 7.77M12 4L2 20h20L12 4z"/>' +
      '<path fill="none" d="M0 0h24v24H0V0z"/>' +
    '</svg>',
    buttonName: 'triangleToolButton',
    tooltip: 'Triangle'
  });

  readerControl.setHeaderItems(function(header) {
    var triangleButton = {
      type: 'toolButton',
      toolName: triangleToolName
    };
    header.get('freeHandToolGroupButton').insertBefore(triangleButton);
  });
});

$(document).on('documentLoaded', function() {
  // set the tool mode to our tool so that we can start using it right away
  readerControl.setToolMode(triangleToolName);
});


Reply all
Reply to author
Forward
0 new messages