How to create zoomable text - openseadragon

I am looking to add zoomable text to the rendered image in OpenSeadragon. The desired functionality is that when you are zoomed out, the text is very small, and as you zoom in, the text increases in size.
I have tried to add text to an overlay div DOM element. This adds text, but as you zoom out, the size of the div gets smaller but the text doesn't. The text gets squeezed to multiple lines.
I use the Render2 class in Angular 7 to manipulate the DOM, but this can be done in any framework/library:
// create the DOM element need for SeaDragon's overlay
let div = this.renderer.createElement('div');
const text = this.renderer.createText(`x: ${xaxis}, y: ${yaxis}`);
// append text to div element
this.renderer.appendChild(div, text);
// add id for Seadragon overlay creation and styling of overlay
this.renderer.setAttribute(div, 'id', overlayname);
this.renderer.setAttribute(div, 'class', 'image-ruler');
// now append the div tag to ViewChild div
this.renderer.appendChild(this.singleOverlay.nativeElement, div);
I expect the text to zoom with the image. Is this possible?

I figured out how to do it. I saw this example that uses an svg plugin so d3 can be used to draw an overlay. After downloading this plugin, I noticed that when I printed text on the screen, it was incredibly large. Too large to read.
As d3 at this zoom level uses the scale of 0-1 for the size of the image, I had to divide the scale by the width of my image.
var p = viewer.viewport.pixelFromPoint(new OpenSeadragon.Point(0, 0), true);
var zoom = viewer.viewport.getZoom(true);
var rotation = viewer.viewport.getRotation();
var imageSize = viewer.world.getItemAt(0).getContentSize();
var scale = viewer.viewport._containerInnerSize.x * zoom / imageSize.x;
g.attr('transform',
'translate(' + p.x + ',' + p.y + ') scale(' + scale + ') rotate(' + rotation + ')');
I then reimplemented the plugin's resize function in my component as not to modify a node module. The result was small text on my enormous image that was readable when you zoom in.

Related

How to place image on right corner of pdf using node js?

I have one pdf file and one image file which is the signature of the client. I need to place the client signature image on the top right corner of every page of pdf. This whole process is in node js so I have used the pdf-lib npm package to attach image on pdf. The current issue I'm unable to place the image on the top-right corner.
Below is the logic which I used to set an image on the top-right corner of the pdf but this logic is not true for every case in some cases when the width of the image or pdf changes then it's not worked as expected. Sometimes images are getting too much small or sometimes too much large.Just because I set fix height and width. As I don't know how to calculate it dynamically
const pages = pdfDoc.getPages();
for (let i = 0; i < pdfDoc.getPageCount(); i++) {
let imagePage='';
imagePage = pdfDoc.getPage(i);
console.log(i+1)
console.log(imagePage.getWidth())
let xx=imagePage.getWidth()
console.log(imagePage.getHeight())
console.log(img.width)
console.log(img.height)
let yy=imagePage.getHeight()
imagePage.drawImage(img, {
x: xx-150,
y: yy-70,
width: 70,
height: 70
})
}
Please check console values below
595.5 ​​​​​at ​​​​​​​​imagePage.getWidth()​​​
842.2499787 ​​​​​at ​​​​​​​​imagePage.getHeight()​​​
1200 ​​​​​at ​​​​​​​​img.width​​​
700 ​​​​​at ​​​​​​​​img.height
please check the above image where I attached the signature to pdf but in this case, the signature image is not properly visible so how can I set the image to pdf on the top-right corner?
How can I calculate the x and y position dynamically for any pdf?
I got the solution to get dynamic x and y values to set an image on the top-right corner of the pdf
let xx=imagePage.getWidth();
console.log(imagePage.getHeight());
console.log("[PAGE_HEIGHT]",imagePage.getHeight());
const imgHeight = img.height;
const imgWidth = img.width;
console.log("[SIGNATURE_IMAGE_WIDTH]",img.width);
console.log("[SIGNATURE_IMAGE_HEIGHT]",img.height);
const marginX = 50;
let yy=imagePage.getHeight();
imagePage.drawImage(img, {
x: xx -marginX-imgWidth,
y: yy- marginX-imgHeight,
width: imgWidth,
height: imgHeight
})
margin is a fixed value to move an image from the top and right sides to get a proper view.
I have removed image height and width from the actual page width and height that will return top right corner.

How to find bit length of text with specific font and font size

I'm developing NativeScript JavaScript code to create dynamic text marker for maps. I have the code working that creates a marker for a specific string. My next step is to take any given string, determine its height and width in bits, and create the marker sized to contain the text.
My problem is finding the size of the text, given the text string itself, the font size, and the font family.
It looks like getMeasuredWidth could work, except that the string must already be loaded on a page before that function will return a value. In my case, I simply need to compute the size; the text won't otherwise appear as such on a page (the text in the marker becomes an image).
Is there a way to do this?
var bmp = BitmapFactory.create(200);
bmp.dispose(function (b) {
try {
b.drawRect(
"100,34", // size
'0,0', // upper-left coordinate
KnownColors.Black, // border color
KnownColors.Cornsilk // fill color
);
b.writeText(
"Parking",
"2,25",
{ color: KnownColors.Black, size: 8, name: 'fontawesome-webfont', });
...
In the code above, the width of "100" of the bounding rectangle actually represents the bit width of "Parking" with a small amount of padding. What I want to does calculate the rectangle's height and width and not hard-code it.
Try this, finding label size without adding it to Page upon button click
export function onFindButtonTap(args: EventData) {
const button = <any>args.object;
const label = new Label();
label.text = "Hello, found my size?"
label.fontSize = 20;
(<any>label)._setupAsRootView(button._context);
label.onLoaded();
label.measure(0, 0);
console.log(`Width : ${label.getMeasuredWidth()} x Height : ${label.getMeasuredHeight()}`);
}
Playground Sample
Note: I didn't get a chance to test it with iOS yet, let me know if you hit any issues.

pdfbox showText width and height setting

I am creating web based app where you annotate a pdf at browser(using pdf.js and fabricjs) and save the annotations at server side (java servlet REST endpoint) using Apache pdfbox (2.0.x). I can overlay images and text using PDPageContentStream's drawImage and showText methods. Also the user may scale the annotations at browser by selecting and dragging it at corners. This is properly scaled for drawImage by adjusting the width parameter based on the scaleX factor set by fabricjs. But for showText I cannot find any options to scale the text. I can adjust the font size, but that won't translate correctly against the corner.
Any thoughts on how to scale (or change height and width) and put a text on pdf using showText method of PDPageContentStream
Thanks in advance
Prem
PDPageContentStream contentStream = new PDPageContentStream(doc, page, AppendMode.APPEND, true, true);
scaleX = signObj.get("scaleX").getAsFloat();
scaleY = signObj.get("scaleY").getAsFloat();
annoType = signObjWrapper.get("annoType").getAsInt();
float x_adjusted = (signObj.get("left").getAsFloat()) + page.getCropBox().getLowerLeftX();
float y_adjusted = -(signObj.get("top").getAsFloat()) + page.getCropBox().getUpperRightY();
if (annoType == SIGN) // this annotation is a sign object
{
contentStream.drawImage(pdImage, x_adjusted,
y_adjusted - (signObj.get("height").getAsFloat() * scaleY),
signObj.get("width").getAsFloat() * scaleX, signObj.get("height").getAsFloat() * scaleY);
contentStream.close();
} else if (annoType == TEXT) {
//Begin the Content stream
contentStream.beginText();
//Setting the font to the Content stream
contentStream.setFont(PDType1Font.COURIER,signObj.get("fontSize").getAsInt());
//Setting the position for the line
contentStream.newLineAtOffset(x_adjusted, y_adjusted - (signObj.get("height").getAsFloat() * scaleY));
//Adding text in the form of string
contentStream.showText(signObj.get("text").getAsString());
//Ending the content stream
contentStream.endText();
//Closing the content stream
contentStream.close();
}

D3 - Positioning tooltip on SVG element not working

I have a webpage with an SVG. On some of its shapes I need to display a tooltip. However, I can't get the tooltip to appear where it should, just some pixels away from the shape itself.
It appears way on the right hand side of the screen, maybe some 300px away.
The code I am using to get the coordinates is as follows:
d3.select("body")
.select("svg")
.select("g")
.selectAll("circle")
.on("mouseover", function(){return tooltip.style("visibility", "visible");})
.on("mousemove", function(){
var svgPos = $('svg').offset(),
/*** Tooltip ***/
//This should be the correct one, but is displaying at all working at all.
/*x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;*/
//This displays a tool tip but way much to the left of the screen.
x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;
Tooltip
window.alert("svgPos: "+svgPos+" top: "+y+"px left: "+x+"px "+d3.event.target.cx.animVal.value);
return tooltip.style("top", x+"px").style("left",y+"px");
})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
I got to this code following this SO post.
I have changed $(ev.target).attr(cx) as it is not returning a value on my machine; d3.event.target.cx is, even though it seems it is not affecting the end result anyway.
What am I doing wrong? Could somebody help me please? Thank you very much in advance for your time.
If your tooltip is an HTML element, then you want to position it relative to the page as a whole, not the internal SVG coordinates, so accessing the cx/cy value is just complicating things. I can't say for sure without looking at your code, but if you have any transforms on your <svg> or <g> elements, then that could be what's throwing you off.
However, there is a much easier solution. Just access the mouse event's default .pageX and .pageY properties, which give the position of the mouse relative to the HTML body, and use these coordinates to position your tooltip div.
Example here: http://fiddle.jshell.net/tPv46/1/
Key code:
.on("mousemove", function () {
//console.log(d3.event);
return tooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Even with rotational transforms on the SVG circles, the mouse knows where it is on the page and the tooltip is positioned accordingly.
There are other ways to do this, including getting a tooltip to show up in a fixed location relative to the circle instead of following the mouse around, but I just checked the examples I was working on and realized they aren't cross-browser compatible, so I'll have to standardize them and get back to you. In the meantime, I hope this gets you back on track with your project.
Edit 1
For comparison, here is the same example implemented with both an HTML tooltip (a <div> element) and an SVG tooltip (a <g> element).
http://fiddle.jshell.net/tPv46/4/
The default mouse event coordinates may be great for positioning HTML elements that are direct children of <body>, but they are less useful for positioning SVG elements. The d3.mouse() function calculates the mouse coordinates of the current event relative to a specified SVG element's coordinate system, after all transformations have been applied. It can therefore be used to get the mouse coordinates in the form we need to position an SVG tooltip.
Key code:
.on("mousemove", function () {
var mouseCoords = d3.mouse(
SVGtooltip[0][0].parentNode);
//the d3.mouse() function calculates the mouse
//position relative to an SVG Element, in that
//element's coordinate system
//(after transform or viewBox attributes).
//Because we're using the coordinates to position
//the SVG tooltip, we want the coordinates to be
//with respect to that element's parent.
//SVGtooltip[0][0] accesses the (first and only)
//selected element from the saved d3 selection object.
SVGtooltip
.attr("transform", "translate(" + (mouseCoords[0]-30)
+ "," + (mouseCoords[1]-30) + ")");
HTMLtooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Note that it works even though I've scaled the SVG with a viewBox attribute and put the tooltip inside a <g> with a transform attribute.
Tested and works in Chrome, Firefox, and Opera (reasonably recent versions) -- although the text in the SVG tooltip might extend past its rectangle depending on your font settings. One reason to use an HTML tooltip! Another reason is that it doesn't get cut off by the edge of the SVG.
Leave a comment if you have any bugs in Safari or IE9/10/11. (IE8 and under are out of luck, since they don't do SVG).
Edit 2
So what about your original idea, to position the tooltip on the circle itself? There are definite benefits to being able to position the tip exactly: better layout control, and the text doesn't wiggle around with the mouse. And most importantly, you can just position it once, on the mouseover event, instead of reacting to every mousemove event.
But to do this, you can no longer just use the mouse position to figure out where to put the tooltip -- you need to figure out the position of the element, which means you have to deal with transformations. The SVG spec introduces a set of interfaces for locating SVG elements relative to other parts of the DOM.
For converting between two SVG transformation systems you use SVGElement.getTransformToElement(SVGElement); for converting between an SVG coordinate system and the screen, you use SVGElement.getScreenCTM(). The result are transformation matrices from which you can
extract the net horizontal and vertical translation.
The key code for the SVG tooltip is
var tooltipParent = SVGtooltip[0][0].parentNode;
var matrix =
this.getTransformToElement(tooltipParent)
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
SVGtooltip
.attr("transform", "translate(" + (matrix.e)
+ "," + (matrix.f - 30) + ")");
The key code for the HTML tooltip is
var matrix = this.getScreenCTM()
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
absoluteHTMLtooltip
.style("left",
(window.pageXOffset + matrix.e) + "px")
.style("top",
(window.pageYOffset + matrix.f + 30) + "px");
Live example: http://fiddle.jshell.net/tPv46/89/
Again, I'd appreciate a confirmation comment from anyone who can test this in Safari or IE -- or any mobile browser. I'm pretty sure I've used standard API for everything, but just because the API is standard doesn't mean it's universally implemented!

Nesting data using D3.js (heatmap)

So I am new to working with Javascript (especially the D3 library) and I am trying to do something not unlike the following example: http://mbostock.github.com/d3/talk/20111116/iris-splom.html. In my case though each cell is the same thing a 4 x 4 grid with exactly the same scale.
So in my case the top level element is a plate. Each plate has rows and columns at the intersection of a row and column a value (a heatmap if you will). I able to create the proper plate elements; however, the data for ALL plates is present under each element rather than properly nested. I tried to attach an image so you can see that each "plate" is the same, if you look at the underlying document structure it is the same and essentially each rectangle is two overlaid data points.
In looking more closely at Mike's example (link above), it looks like he uses a cross function to help out with the nesting of data, I am wondering if that is where my code falls down. Thank you for any help you all can provide.
I have posted my code below
d3.csv("plateData.csv", function(data) {
var m = 20,
w = 400,
h = 300,
x_extent = d3.extent(data, function(d){return d.Rows}),
y_extent = d3.extent(data, function(d){return d.Columns}),
z_extent = d3.extent(data, function(d){return d.Values});
var x_scale = d3.scale.linear()
.range([m, w-m])
.domain(x_extent)
var y_scale = d3.scale.linear()
.range([h-m,m])
.domain(y_extent)
var ramp=d3.scale.linear()
.domain(z_extent)
.range(["blue","red"]);
// Nest data by Plates
var plates = d3.nest()
.key(function(d) { return d.Plate; })
.entries(data);
// Insert an svg element (with margin) for each plate in our dataset.
var svg = d3.select("body").selectAll("svg")
.data(plates)
.enter().append("svg:svg")
.attr("width", w)
.attr("height", h)
//add grouping and rect
.append("svg:g")
.selectAll('rect')
.data(data)
.enter()
.append('svg:rect')
.attr('x', function(d){return x_scale(d.Rows)})
.attr('y', function(d){return y_scale(d.Columns)})
.attr('width', 10)
.attr('height', 10)
.style('fill', function(d){return ramp(d.Values)});
}
);
AND example Data:
Plate,Rows,Columns,Values
12345,1,1,1158.755
12345,1,2,1097.768
12345,1,3,1097.768
12345,1,4,914.807
12345,2,1,1189.249
12345,2,2,1128.261
12345,2,3,1433.197
12345,2,4,701.352
12345,3,1,914.807
12345,3,2,1433.197
12345,3,3,1189.249
12345,3,4,1402.703
12345,4,1,1158.755
12345,4,2,1067.274
12345,4,3,701.352
12345,4,4,1372.21
56987,1,1,20.755
56987,1,2,97.768
56987,1,3,97.768
56987,1,4,14.807
56987,2,1,89.249
56987,2,2,28.261
56987,2,3,33.197
56987,2,4,15.352
56987,3,1,2000.807
56987,3,2,14.197
56987,3,3,89.249
56987,3,4,402.703
56987,4,1,158.755
56987,4,2,3067.274
56987,4,3,701.352
56987,4,4,182.21
You problem has two sources:
You are inserting one svg element for each nested group, but you don't have defined a position for each element (all the elements are in the body).
You are selecting all the svg elements in the body, and appending one rectangle for each data element, without considering the nested structure.
One solution is to create one svg element inside the body, one group for each plate, define a position for each plate, translate the group to its position (in the svg element) and then create the rectangles for the heatmap.

Resources