2 Physically same Document, different behaviour

Hi team

I have 2 PDF documents that physically having the same layout and rotation. Document A is generated from MS Office, while Document B is apparently generated through printer or scanner. In the WebViewer, inside the left panel, we add several custom panel that contain some lists of data fetched from backend. When these list item element gets clicked, they would generate a custom stamp that would then inserted into the document in coordinates relevant to the position of the clicked element. This feature has been used without any troubles for some time, until Document B comes along. Nothing happens when testers try our custom annotations in this document. While the annotations themselves are indeed saved into DB without errors, seems like the problem is in the Document B layout (this is my initial thought), because the feature will working as expected when Document B gets rotated into LANDSCAPE. I try to get the document’s rotation info from both documents using getRotation() and getCompleteRotation() for first page, and Document A have the same value for both functions (0,0) while Document B has different values (0, 3). Seems like this is why Document B has behave differently?

Here’s my code for those custom left panel elements’ click event:

`
var annotManager = docViewer.getAnnotationManager();
var stampAnnot = new annotations.StampAnnotation();
var svgWidth = element.querySelector(’#wpReferenceLabel’).clientWidth / 3; // Get the clicked element’s width
var pdfCoords = pdfUtils.calculateDropInCoordinatesByCustomLeftPanelClick(originalEvent, docViewer); // Calculate where to drop this stamp in the current PDF page
var dropIn = pdfCoords.y * docViewer.getZoom();
var stamp = pdfUtils.initCustomStamp(
annotManager.getCurrentUser(),
docViewer.getCurrentPage(),
svgWidth,
55,
“wpReference”,
{ uri: element.dataset.wplink, ref: element.dataset.wpref, code: element.dataset.wpcode, ypos: dropIn, xpos: pdfCoords.x },
stampAnnot
); // Create our custom stamp

var svgContent = pdfUtils.createSVGForLeftPanelStamp(‘wp’, svgWidth, 55, fileInfo, element); // Make a SVG content out from the element’s text string
setTimeout(pdfUtils.convertSVG2PNG64(‘workpaperStampCanvas’, ‘data:image/svg+xml;utf-8,’ + svgContent, 100, 55, function (s) {
pdfUtils.redrawLeftPanelStampImage(s, null, null, dropIn, stamp, annotManager); // Convert the SVG to PNG then redraw the stamp content
}), 0);
`

Here is the code for calculating the drop position:
`
const calculateDropInCoordinatesByCustomLeftPanelClick = function (originalEvent, docViewer) {
var scrollElement = docViewer.getScrollViewElement();
var scrollLeft = scrollElement.scrollLeft || 0;
var scrollTop = scrollElement.scrollTop || 0;
// originalEvent is the click event
var coords = {
x: originalEvent.pageX + scrollLeft,
y: (originalEvent.pageY - 50) + scrollTop,
};
var displayMode = docViewer.getDisplayModeManager().getDisplayMode();
var page = displayMode.getSelectedPages(coords, coords);
var targetPage = (page.first !== null) ? page.first : docViewer.getCurrentPage() - 1;
var calculatedCoords = displayMode.windowToPage(coords, targetPage);

return calculatedCoords;
}
`

calculatedCoords above would be used by final function redrawLeftPanelStampImage to do the stamp annotation redraw:
const redrawLeftPanelStampImage = function(imageData, width, height, dropPos, stampAnnot, annotManager, rotation) { stampAnnot.ImageData = imageData; if (width !== null) { stampAnnot.Width = width; } if (height !== null) { stampAnnot.Height = height; } stampAnnot.Custom.imagedata = imageData; stampAnnot.Y = 200; stampAnnot.Y = dropPos; if (rotation && (rotation > 0)) { stampAnnot.Rotation = rotation; stampAnnot.Height = width; stampAnnot.Width = height; } // Update this annotation, then redraw annotManager.updateAnnotation(stampAnnot); annotManager.addAnnotation(stampAnnot); annotManager.redrawAnnotation(stampAnnot); };

While the initCustomStamp is just a function to create a new stamp annotation instance:
const initCustomStamp = function(author, pageNumber, width, height, subject, customData, stampAnnot) { stampAnnot.Author = author; stampAnnot.setPageNumber(pageNumber); stampAnnot.Width = width; stampAnnot.Height = height; stampAnnot.NoRotate = true; stampAnnot.Subject = subject; stampAnnot.Custom = customData; return stampAnnot; };

Here’s what this feature should behave, when working with Document A (I have include a GIF animation in the compressed attachment for better illustrate the feature):

But here’s how Document B behave:

After digging around, I am pretty sure that my calculateDropInCoordinatesByCustomLeftPanelClick function is not equipped with layout detection or something like that, which means Document A and Document B MUST BEHAVE THE SAME (cmiiw), no? But I do utilize the getScrollViewElement() to get the scrollable area and this is likely why all thing falls apart for Document B. When I inspect #DocumentViewer element (returned from getScrollViewElement()), I found out that Document A and B indeed have a different layout/styling for each of their pageWidgetContainer0, where Document A (with rotation 0 and completeRotation 0) has:

position: absolute; top: 0px; left: 0px; width: 595px; height: 842px; z-index: 40; transform: matrix(1, 0, 0, 1, 0, 0); transform-origin: left top; overflow: hidden;

while Document B (with rotation 0 and completeRotation 3) has:

position: absolute; top: 0px; left: 0px; width: 842px; height: 595px; z-index: 40; transform: matrix(0, -1, 1, 0, 0, 842); transform-origin: left top; overflow: hidden;

Perhaps this is the root of the problem we had with Document B? I try to manually alter this styling in Document B and apply scrollViewUpdated() but fails hard.

Several questions I’d like to discuss regarding this problem:

  1. Why PDFTron give a different rotation and completeRotation value for Document B which physically have the same layout with Document A?
  2. Is that difference between rotation and completeRotation really the source of the problem?
  3. How/what the best way to deal with this kind of problem? I have tried to measure the PDF coordinates then convert it to PageCoordinates and to Window and try basically everything I can get from https://www.pdftron.com/documentation/web/guides/coordinates/ but no luck. I am thinking about somehow alter or change the rotation value but to do that, something like alter the rotation to have the same value (0,0) but I think that’s cheating and dirty.

I have attached the two documents with some screenshot for you guys. Please assist me on this, thank you.

Scanned.7z (1.44 MB)

Hi,

Yes correct, the issue is most likely related to document B having a different complete rotation compared to document A.

  1. Why PDFTron give a different rotation and completeRotation value for Document B which physically have the same layout with Document A?

Please see this guide on complete rotation vs. rotation in case you haven’t done so: https://www.pdftron.com/documentation/web/guides/manipulation/rotate?searchTerm=complete%20rotation .
I suspect there might have been some embedded / built in rotation done on this document before it was loaded into WebViewer (hence the complete rotation of 3 which equates to 270 degrees).
I’m going to guess, document B; when it was print or scanned, it was scanned at 90 degrees (landscaped), and since it didn’t look portrait, it must have been rotated 3 times so that it looked at 0 degrees (hence the complete rotation of 3).

Rotation is where its only done by webviewer UI when you click on that rotation icon.

  1. Is that difference between rotation and completeRotation really the source of the problem?

Adding on what was said from the above, I observe that your stamps are drawn with a rotation of 270 degrees (rotation of 3), so yes it is most likely.

  1. How/what the best way to deal with this kind of problem?

I think it might vary.
You can:

  • try to standardize things such that everything has the same complete rotation of 0
  • work in that rotated coordinate space (which could get really messy); https://en.wikipedia.org/wiki/Rotation_matrix by transforming the points.

From inspecting your code snippet and playing around, please give this a try:

For this line of code, please pass in the complete rotation value:
setTimeout(pdfUtils.convertSVG2PNG64(‘workpaperStampCanvas’, ‘data:image/svg+xml;utf-8,’ + svgContent, 100, 55, function (s) {
pdfUtils.redrawLeftPanelStampImage(s, null, null, dropIn, stamp, annotManager, docViewer.getCompleteRotation(pageNumber));
}), 0);

For this line of code:
if (rotation && (rotation > 0)) {
stampAnnot.Rotation = rotation * 90;
// swap the width and height only if rotation has value of 1 or 3 else it looks weird due to the CSS done on the canvas when at rotation 1 or 3
}

Please let me know if you are still confused about the answer to 1 or 2 or if my suggestion to 3 does not work.

Thanks,

Anthony Chen

Software Developer
PDFTron Systems Inc.

Hi, I am sorry I was working on other stuff during my absence from this discussion.

Thank you very much for your reply. I have read the Wikipedia link you provide and looks like it has been provided (matrix calculation and points) as a build in functions discussed in https://www.pdftron.com/documentation/web/guides/coordinates/ ? Cmiiw

And you are probably right about how many times this document has been rotated and that value carried out to PDFTron.

Also thank you, I have tried your solution but unfortunately it didn’t work for me. During my further investigation however, I’ve started to wondering is it doable for me to alter the scrollView element’s dimension instead? From my understanding, scrollView elements is being used to lay down the canvas used by PDFTron to place annotations objects? Is this make sense at all? The idea is to implement a ‘standard’ dimensions of the scrollView elements for all document (or perhaps just for certain documents that needed to be standardize). Perhaps something like:

`
var defaultPDFScrollViewStyling = {
‘position’: ‘absolute’,
‘top’: ‘0px’,
‘left’: ‘0px’,
‘width’: ‘595px’,
‘height’: ‘842px’,
‘z-index’: ‘40’,
‘transform’: ‘matrix(1, 0, 0, 1, 0, 0)’,
‘transform-origin’: ‘left top’,
‘overflow’: ‘hidden’
}; // These are set of CSS sytle I fetch from ‘normal’ PDF document

const scrollElement = docViewer.getScrollViewElement();
var widgetContainers = $(scrollElement).find(’#pageWidgetContainer0’); // first page
$(widgetContainers).css(defaultPDFScrollViewStyling);
this.docViewer.scrollViewUpdated();
`

I have tried this way but the CSS values (width, height, transform) is keep altered… I wonder if this is a correct way.

But the most important question I think is, is this worth a try.

Would love to hear from you soon. TIA!

Hi,

Would you be able to elaborate what problems you ran into with the suggested code solution?
I believe as long as you can get the stampAnnot.Rotation to have the proper and correct value, it should work.

Altering the matrix of the scroll view element may not be recommended nor is it a good idea.
By going with this route, this may potentially lead to managing a lot of the values yourself which can get tedious.

When I get the chance, I can send you a sample code snippet that seems to have worked for my case.

Looking forward to your reply.

Anthony Chen

Software Developer
PDFTron Systems Inc.

Hi,

Have attached a zip file with GIF image to illustrate what the result of your recommendation.

For my part, your solution is working partially because the annotation actually gets rotated (thank you!)
But the main problem as you can probably see in the GIF, its like facing the odd direction.

I made two attempts in that screen recording, using your code, first I tried to place the annotation without scrolling down the view. In normal document, this working fine. For this document however, the annotation will be placed (or visibly placed) only when I scroll the document down to the half part of it. Thats why I started to wondering about scrollView (my point of view is like what I stated in my previous reply) element but I could be wrong–but yeah from the looks of it, I agree that it would be a mess.

Yes please, if you do have a working code, I would like to try it, just in case I have miss something!

Thank you very much for your kind help. Cheers.

layout-issue-rotated-but-wrong-coordinates-perhaps.zip (424 KB)

Hi,

Thank-you for providing a bit more clarity on the issue.
I am able to see in your .gif that when you click the first time, nothing appears.

I’m guessing you are working with the document that already has rotation and you have rotated it back?

I’m not sure why the annotation is not added on your very first click.
Seeing how it added further down the document, I would expect the annotation to be seen as you scrolled further. Yet it got added when you clicked a second time.

The only thing that comes to my mind is the altering of the y-coordinate in your code:
var dropIn = pdfCoords.y * docViewer.getZoom();

Looking at calculateDropInCoordinatesByCustomLeftPanelClick , you are returning back the viewer page coordinate. If my memory is correct, with viewer coordinates, it will already handle the zoom / scale / rotation.

Please try getting rid of the docViewer.getZoom() and see if that fixes your issue.

Here is a sample code I tried:
I used this code in https://www.pdftron.com/samples/web/samples/viewing/viewing/
Ensure that you have a document with a complete rotation of 0 and another document with a complete rotation of 1.

  1. create an html button element with an id

const sampleDataUrl = “data:image/gif;base64,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”;

document.getElementById(‘a’).addEventListener(‘click’, () => {
const stampAnnot = new instance.Annotations.StampAnnotation();
stampAnnot.PageNumber = 1;
// stampAnnot.X = rotatedX(20, 10, Math.PI/ 2);
// stampAnnot.Y = rotatedY(20, 10, Math.PI/ 2);
stampAnnot.X = 20;
stampAnnot.Y = 10;
stampAnnot.Width = 275;
stampAnnot.Height = 40;
stampAnnot.Rotation = 90;
stampAnnot.ImageData = sampleDataUrl;
stampAnnot.Author = instance.annotManager.getCurrentUser();

instance.annotManager.addAnnotation(stampAnnot);
instance.annotManager.redrawAnnotation(stampAnnot);
});

Observations I have noticed:

  • if I omit “Rotation”, it seems to correct itself to to correct rotation. I assume it might be a new change we added
  • X and Y are fixed values, and the stamp is rendered at the correct position regardless of viewer / complete rotation (please see point about viewer coordinate above)

Please give those suggestions and observations a try.

Anthony Chen

Software Developer
PDFTron Systems Inc.