I spent a long time trying to center a FreeText annotation on a page programmatically and kept getting inconsistent results depending on the page's rotation. Posting the findings here in case it saves someone else the headache.
The problem:
getPageBox("Crop", page) returns the page box in what Acrobat calls "Rotated User Space" — i.e., matching what you see on screen. But addAnnot's rect parameter does not consistently use that same space. On a page with rotation = 0, the origin (0,0) of the annotation coordinate system coincided with the center of my page (not a corner, as I expected). On a page with rotation = 90 in the same document, neither (0,0), nor (w/2, h/2), nor the axes swapped gave the correct center — no simple formula worked.
The fix:
The Matrix2D object with .fromRotated(doc, page) and .transform(pts) converts coordinates from Rotated User Space (what getPageBox returns) into the space addAnnot actually needs. This isn't officially documented for PDF-XChange scripting, but it works (found the underlying implementation referenced in an old forum thread and confirmed it empirically):
if (typeof Matrix2D === "undefined") {
function Matrix2D(a,b,c,d,h,v) {
this.a=a||1; this.b=b||0; this.c=c||0; this.d=d||1; this.h=h||0; this.v=v||0;
this.concat=function(m){return new Matrix2D(this.a*m.a+this.b*m.c,this.a*m.b+this.b*m.d,this.c*m.a+this.d*m.c,this.c*m.b+this.d*m.d,this.h*m.a+this.v*m.c+m.h,this.h*m.b+this.v*m.d+m.v);};
this.rotate=function(rad){return this.concat(new Matrix2D(Math.cos(rad),Math.sin(rad),-Math.sin(rad),Math.cos(rad),0,0));};
this.translate=function(x,y){return this.concat(new Matrix2D(1,0,0,1,x,y));};
this.transform=function(pts){var out=[];for(var i=0;i<pts.length;i+=2){out.push(pts*this.a+pts[i+1]*this.c+this.h);out.push(pts*this.b+pts[i+1]*this.d+this.v);}return out;};
this.fromRotated=function(doc,page){page=page||0;var cb=doc.getPageBox("Crop",page),mb=doc.getPageBox("Media",page);var mh=mb[1]-mb[3],mw=mb[2]-mb[0];var rot=doc.getPageRotation(page);var m=new Matrix2D(1,0,0,1,cb[0]-mb[0],cb[3]-mb[3]);if(rot===90)return this.concat(m.rotate(Math.asin(1)).translate(mh,0));if(rot===180)return this.concat(m.rotate(2*-Math.asin(1)).translate(mw,mh));if(rot===270)return this.concat(m.rotate(-Math.asin(1)).translate(0,mw));return this.concat(m);};
}
}
var doc = this, page = doc.pageNum;
var rot = doc.getPageRotation(page);
var box = doc.getPageBox("Crop", page);
var cx = (box[0]+box[2])/2, cy = (box[1]+box[3])/2;
var rect;
if (rot === 90 || rot === 270) {
var rectVisual = [cx-50, cy-50, cx+50, cy+50];
var m = (new Matrix2D).fromRotated(doc, page);
rect = m.transform(rectVisual);
} else {
rect = [-50, -50, 50, 50];
}
doc.addAnnot({ page: page, type: "Square", rect: rect, fillColor: ["RGB",1,0,0], strokeColor: ["RGB",0,0,0], width: 2 });
Tested on both a 0°-rotated page and a 90°-rotated page in the same document set — worked correctly on both. Not 100% sure this generalizes to every PDF (our origin-at-center behavior for rotation 0 was surprising and might be specific to how our CAD software exports PDFs), so worth testing on your own files, but hopefully this saves someone the trial-and-error we went through.
addAnnot rect uses different coordinate origin than getPageBox when page rotation ≠ 0 — Matrix2D fix
Moderators: PDF-XChange Support, Daniel - PDF-XChange, Chris - PDF-XChange, Sean - PDF-XChange, Paul - PDF-XChange, Vasyl - PDF-XChange, Ivan - Tracker Software, Stefan - PDF-XChange
Forum rules
DO NOT post your license/serial key, or your activation code - these forums, and all posts within, are public and we will be forced to immediately deactivate your license.
When experiencing some errors, use the IAUX_Inst::FormatHRESULT method to see their description and include it in your post along with the error code.
DO NOT post your license/serial key, or your activation code - these forums, and all posts within, are public and we will be forced to immediately deactivate your license.
When experiencing some errors, use the IAUX_Inst::FormatHRESULT method to see their description and include it in your post along with the error code.
-
AKD2308
- User
- Posts: 1
- Joined: Mon Dec 22, 2025 9:34 am