mapping highlights/annotations to text in pdf - parsing

So i have this sample pdf file with three words on separate lines:
"
hello
there
world
"
I have highlighted the word "there" on the second line. Internally, within the pdf, i'm trying to map the highlight/annotation structure to the text (BT) area.
The section corresponding to the word "there" looks like so:
BT
/F0 14.6599998 Tf
1 0 0 -1 0 130 Tm
96 0 Td <0057> Tj
4.0719757 0 Td <004B> Tj
8.1511078 0 Td <0048> Tj
8.1511078 0 Td <0055> Tj
4.8806458 0 Td <0048> Tj
ET
I also have an annotation section where I have my highlight which has the following rect dimensions:
18 0 19 15 20 694 21 786 22 853 23 1058 24 1331 [19 0 R 20 0 R]<</AP<</N 10 0 R>>
...
(I left the top part of the annotation out on purpose because it is long. I extracted what i thought were the most important parts.
Rect[68.0024 690.459 101.054 706.37]
I'm kind of confused about how my text is mapped to this one highlight that I have. The coordinates do not seem to match (130 y vs 690 y)? Am I looking in the right place and interpreting my text and/or highlight annotation coordinates correctly?
Update:
i want to add more info on how I created this test pdf.
Its pretty simple to recreate the pdf. I went to google docs and created an empty document. On three lines i wrote my text as described above. I downloaded that as a pdf and then opened it in adobe acrobat reader DC (the newest one i think). I then used adobe acrobat reader to highlight the specified line and re save it. After that I used some python to unzip the pdf sections.
The python code to decompress the pdf sections:
import re
import zlib
pdf = open("helloworld.pdf", "rb").read()
stream = re.compile(r'.*?FlateDecode.*?stream(.*?)endstream', re.S)
for s in stream.findall(pdf):
s = s.strip('\r\n')
try:
print(zlib.decompress(s))
print("")
except:
pass

Unfortunately the OP only explained how he created his document and did not share the document itself. I followed his instructions but the coordinates of the annotation differ. As I only have this document for explanation, though, the OP will have to mentally adapt the following to the precise numbers in his document.
The starting coordinate system
The starting (default) user coordinate system in the document is implied by the crop box. In the document at hand the crop box is defined as
/CropBox [0 0 596 843]
i.e. the visible page is 596 units wide and 843 units high (given the default user unit of 1/72" this is an A4 format) and the origin is in the lower left corner. x coordinates increase to the right, y coordinate increase upwards. Thus, a coordinate system as usually started with in math, too.
The annotation rectangle
This also is the coordinate system of the annotation rectangle coordinates.
In the case at hand they are
/Rect [68.0595 741.373 101.138 757.298]
i.e. the rectangle with the lower left corner at (68.0595, 741.373) and the upper right at (101.138, 757.298).
Transformations of the coordinate system
In the page content stream up to the text object already identified by the OP the coordinate system gets transformed a number of times.
Mirroring, translation
In the very first line of the page content
1 0 0 -1 0 843 cm
This transformation moves the origin up by 843 units and mirrors (multiplies by -1) the y coordinate.
Thus, now be have a coordinate system with the origin in the upper left and y coordinate increasing downwards.
Scaling
A bit later in the content stream the coordinate system is scaled
.75062972 0 0 .75062972 0 0 cm
Thus, the coordinate units are compressed to about 3/4 of their original width and height, i.e. each unit along the x or y is only 1/96" wide/high.
The text "there"
Only after these transformations have been applied to the coordinate system, the text object identified by the OP is drawn. It starts by setting and changing the text matrix:
1 0 0 -1 0 130 Tm
This sets the text matrix to translate by 130 units in y direction and mirroring y coordinates once again. (Mirroring back again is necessary as otherwise the text would be drawn upside down.)
96 0 Td
This changes the text matrix by moving 96 units along the x axis.
And the starting point where the text is drawn is at the origin of the coordinate system first changed by the mirroring and translation, and then by scaling of the current transformation matrix, and then by mirroring and translation according to the text matrix.
Does it match?
Which coordinate would this point be in the default user coordinate system?
x = (0 + 96) * .75062972 = 72 (approximately)
y = (((0 * (-1)) + 130) * .75062972) * (-1) + 843 = 745,4 (approximately)
This matches with the annotation rectangle (see above) with x coordinates between 68.0595 and 101.138 and y coordinates between 741.373 and 757.298.
So
I'm kind of confused about how my text is mapped to this one highlight that I have. The coordinates do not seem to match (130 y vs 690 y)? Am I looking in the right place and interpreting my text and/or highlight annotation coordinates correctly?
The coordinates do match, you merely have to make sure you apply the transformations of the current transformation matrix and the text matrix.

Related

change the origin of coordinates and match two coordinates with each other in the Lua language

I'm working on a project in which an + sign moves in animation according to the x and y coordinates on the page
Everything is fine so far.
Now I want to raise two issues: The first issue is that the beginning of the coordinate axis or point (0,0) is in the upper left corner of the page. I need to be able to move this coordinate origin in the four corners of the page if needed.
Problem 2: The + sign that moves in animation varies from 0 to 1400 in the x coordinates and from 0 to 900 in the y coordinates, while I have to display this value on the 340x290 page, how do these coordinate matches work?
Programming is in lua language.
Edit 1:
Screenshot
This sign (+) should move on the specified screen with dimensions of 290x340, according to the values โ€‹โ€‹of x and y at the top right of the screen. The values โ€‹โ€‹x and y shown at the top right are in millimeters, which can be from 0 to 900 mm for x and 0 to 1500 mm for y.
How to match a 0 to 900mm and 0 to 1500mm motion on a 290x340 pixel screen?
Thanks for each reply

How to only buffer outside of the polygon and leave the shared edge un-buffered?

What I am trying to achieve is to buffer a set of connecting polygons while leaving the shared edges un-buffered.
After some searching, ArcGIS's buffer wizards + dissolve does exactly that but unfortunately without an Arcgis license I am searching for a solution that uses QGIS or other platforms such as PostGIS.
The image below shows the result using ArcGIS's buffer wizards + dissolve
Desired Result:
The main challenge is to cut the overlaps from two adjacent buffers into two disjoint polygons along a line equidistant to the unbuffered polygons.
Adding an modified image from JFK to elaborate on my desired result with an example, black shaded polygons are the original polygon, and polygon A and B are the seperate buffered polygons that has three buffered sides, and one un-buffered side (the side where A, B touches)
The doc says the wizard starts by dissolving the polygons, then a buffer is applied on the output.
You can do the same in PostGIS: st_collect aggregates the geometries together, creating a multi-polygon if geometries are disjoint. st_buffer does the buffering (in CRS unit), st_dump will explode the single multi-part buffer into individual polygons.
WITH src(geom) AS (values
('POLYGON((0 0,0 10,10 10, 10 0, 0 0))'::geometry),
('POLYGON((0 0,0 10,-10 10, -10 0, 0 0))'::geometry),
('POLYGON((20 20,20 30,30 30, 30 20, 20 20))'::geometry))
SELECT st_asText(
(st_dump(
st_buffer(
st_collect(geom),
1)
)).geom)
FROM src;
---------
POLYGON((20 19,19.8049096779839 19.0192147195968,19.6173165676349 19.0761204674887,19.4444297669804 19.1685303876975,19.2928932188135 19.2928932188135,19.1685303876975 19.4444297669804,19.0761204674887 19.6173165676349,19.0192147195968 19.8049096779839,19 20,19 30,19.0192147195968 30.1950903220161,19.0761204674887 30.3826834323651,19.1685303876975 30.5555702330196,19.2928932188135 30.7071067811865,19.4444297669804 30.8314696123025,19.6173165676349 30.9238795325113,19.8049096779839 30.9807852804032,20 31,30 31,30.1950903220161 30.9807852804032,30.3826834323651 30.9238795325113,30.5555702330196 30.8314696123025,30.7071067811865 30.7071067811865,30.8314696123025 30.5555702330196,30.9238795325113 30.3826834323651,30.9807852804032 30.1950903220161,31 30,31 20,30.9807852804032 19.8049096779839,30.9238795325113 19.6173165676349,30.8314696123025 19.4444297669804,30.7071067811865 19.2928932188135,30.5555702330196 19.1685303876975,30.3826834323651 19.0761204674887,30.1950903220161 19.0192147195968,30 19,20 19))
POLYGON((0 -1,-10 -1,-10.1950903220161 -0.980785280403231,-10.3826834323651 -0.923879532511287,-10.5555702330196 -0.831469612302547,-10.7071067811865 -0.70710678118655,-10.8314696123025 -0.555570233019605,-10.9238795325113 -0.382683432365094,-10.9807852804032 -0.195090322016134,-11 0,-11 10,-10.9807852804032 10.1950903220161,-10.9238795325113 10.3826834323651,-10.8314696123025 10.5555702330196,-10.7071067811865 10.7071067811865,-10.5555702330196 10.8314696123025,-10.3826834323651 10.9238795325113,-10.1950903220161 10.9807852804032,-10 11,0 11,10 11,10.1950903220161 10.9807852804032,10.3826834323651 10.9238795325113,10.5555702330196 10.8314696123025,10.7071067811865 10.7071067811865,10.8314696123025 10.5555702330196,10.9238795325113 10.3826834323651,10.9807852804032 10.1950903220161,11 10,11 0,10.9807852804032 -0.195090322016128,10.9238795325113 -0.38268343236509,10.8314696123025 -0.555570233019602,10.7071067811865 -0.707106781186547,10.5555702330196 -0.831469612302545,10.3826834323651 -0.923879532511287,10.1950903220161 -0.98078528040323,10 -1,0 -1))
(2 rows)

How to draw tiles on Map from Database?

I am developing a GIS based iOS application using Swift 3.0. I want to draw tiles on Map and tiles images are stored in SQLite Database.
My question is how can I retrieve tile image from database and draw thoses image on Map, as database contains columns zoom (values will be 12, 13, etc.), tile_row (values will be 4122, 3413, etc.), tile_column (values will be 4122, 3413, etc.) and data but I get zoom level value in thousands and I get latitude and longitude values in iOS app, so I need to convert these values to match values in database. I found a way to convert zoom level to 1 to 18 scale but I don't know can I match the tile_row and tile_column value using latitude and longitude.
Also please verify that my code which convert zoom level to 1 to 18 (similar to google map zoom level) is correct:
let zoomLevel = Int(log2(360 / MKCoordinateRegionForMapRect(mapRect).span.longitudeDelta))
Thanks.
In IOS (and most mapping systems) the tile images are stored in a directory structure where each zoom level is a parent directory named with the zoom-level number. Under that directory, there are one or more directories named with the longitudinal tile numbers of the tiles below - e.g., at zoom level 10 there are 1024 tiles across and your directories could be 750, 751, 752, and 753 if that's where their images fell relatively. Under each longitude (x-coordinate) directory are the images (256 X 256 pixels) for that y-coordinate, each named for the x tile coordinate, again out of 1024 at zoom level 10.
To find where you are in those ranges, use MKMapPointForCoordinate(CLLocationCoordinate2D), which will give you the lat (y) and lon (x) map points of a location when fully zoomed out. To get the longitude (y) tile number use:
Int((pow(2.0, Double(z)) as Double) * point.y / 268435456.0)
...where the big number is the total number of points on the x-axis at zoom level 0 (2^20 tiles * 256 pixels / tile). That way if point.x is 1/3 of the big number, the image is the tile 1/3 of the way through 1024, and the integer representing the 256-pixel interval (i.e., tile) that number falls into is the name of the directory.
The latitude (y) map point and tile number are calculated similarly, and that number is the name of the image file. So, at zoom level 10 the image for the tile 752 out of 1024 along the x-axis and 486 out of 1024 along the y-axis would be in the file:
...Documents/Maps/yourDirectory/10/752/486.png
...provided you name your overall map directory Maps and the specific directory for this set of tiles yourDirectory. When you use the overlay, you'd use this directory information along with the rest of the setup to instantiate an MKTileOverlay object. Note that the offsets are from the bottom left corner unless you specify that they're reversed since they're thinking x and y axes (remind you of using CoreGraphics for a UIImage?).
Finally, here's how I calculate the zoom level given two corner points of a region that I want to capture a snapshot for:
let position1 = MKMapPointForCoordinate(bottomRight)
let position2 = MKMapPointForCoordinate(topLeft)
let xPosition1 = position1.x / Setting.shared.mapScale
let xPosition2 = position2.x / Setting.shared.mapScale
let yPosition1 = position1.y / Setting.shared.mapScale
let yPosition2 = position2.y / Setting.shared.mapScale
let relativeSpanX = xPosition1 - xPosition2 // X distance between points relative to size of full map
let relativeSpanY = yPosition1 - yPosition2 // Y distance between points relative to size of full map
let spanForZoom = max(relativeSpanX, relativeSpanY)
startingZoom = max(10, Int(log2(1.0 / spanForZoom)) - 1)
That gets you the zoom level for a tile that fits the size of the area that includes both points, but note that no one standard tile of that size (or any size less than the full map) may include those two points depending on where they lie relative to the grid. For example if they span the prime meridian the first step to 4 tiles at zoom level 1 will separate them, so you may need 2 - 4 tiles of the starting zoom size to get both. Ideally, write a function that tells you the tile number (x and y) that includes a CLLocationCoordinate2D since that gets very handy as you pick, download, and collect your tiles.
While you can get away with a geometrical approach like you're showing to calculate longitudinal / y-axis values, MKMapPointForCoordinate() is indispensable for latitude / y-axis calculations since the mercator map is non-linear as you move north or south, and the function takes care of that for you.
That should get you started, but it's a picky process - one thing to focus on is the fact that the layout is always from the lower left; it's easy to get confused as you gather and label the tiles.
I use the following function to calculate the x and y coordinates for the tile that a point is in for a given zoom level:
func getTileCoordinates(location: CLLocationCoordinate2D, z: Int) -> (x: Int, y: Int)
{
let point = MKMapPointForCoordinate(location)
let locationX = Int((pow(2.0, Double(z)) as Double) * point.x / 268435456.0)
let locationY = Int((pow(2.0, Double(z)) as Double) * point.y / 268435456.0)
return (locationX, locationY)
}
...again, where 268435456.0 is the total number of pixels in the zoom level 20 map along the x or y axis. Note, all of this is for Apples MapKit maps and the functions to display them.

How to get the accurate font size(height) in pdf

I have a sample pdf (attached), and it includes a text object and a rectangle object that have almost the same height. Then I checked the content of the pdf by using itextrup as below:
1 1 1 RG
1 1 1 rg
0.12 0 0 0.12 16 50 cm
q
0 0 m
2926 0 l
2926 5759 l
0 5759 l
0 0 l
W
n
Q
1 1 1 RG
1 1 1 rg
q
0 0 m
2926 0 l
2926 5759 l
0 5759 l
0 0 l
W
n
/F1 205.252 Tf
BT
0 0 0 RG
0 0 0 rg
/DeviceGray CS
/OC /oc1 BDC
0 -1 1 0 1648 5330 Tm
0 Tc
100 Tz
(Hello World) Tj
ET
Q
q
0 0 m
2926 0 l
2926 5759 l
0 5759 l
0 0 l
W
n
0 0 0 RG
0 0 0 rg
/DeviceGray CS
6 w
1 j
1 J
1649 5324 m
1649 4277 l
1800 4277 l
1800 5324 l
1649 5324 l
S
EMC
Q
Obviously the user space matrix is determined by [0.12 0 0 0.12 16 50], and the height for the rectangle is (1800-1649)*0.12*1=18.12, and for the font size I use 205.252*0.12=24.63024. Since the two values are not close, my problem is how to get the height/size of the font?
sample.pdf
OK - I took a look at your file and you're basically hosed. That's the scientific answer, now let me clarify :)
Bad PDF!
The PDF you have up there as a sample contains a font that is not embedded. That "/F1 Tf" command you have there points to the font "ArialMT" in the resources dict for that page. Because the font has not been embedded, you only have two options:
Try to find the actual font on the system and extract the necessary information from there.
Live with the information in the PDF. Let's start with that.
Font Descriptor
Here is an image from pdfToolbox examining the font in the PDF file (caution: I'm associated with this tool):
I've cut off some of the "Widths" table, but other than that this is all of the information you have in the PDF document for this font. And this means you can access the widths for each glyph, but you don't have access to the heights of each glyph. The only information you have regarding heights is the font bounding box which is the union of all glyph bounding boxes. In other words, the font bounding box is guaranteed to be big enough to contain any glyph from the font (both horizontally and vertically).
System Information
You don't say why you need this information so it becomes a little harder to advise further. But if you can't get the information from the PDF, you're only option is to live with the inaccurate information from the PDF or to turn to the system your code is running on to get you more.
If you have the ArialMT font installed, you could basically try to find the font file and then parse the TrueType font file to find the bounding boxes for each glyph. I've done that, it's not funny.
Or you can see if your system can't provide you with the information in a better way. Many operating systems / languages have text calls that can get accurate measurements for you. If not, you can brute force it by rendering the text you want in black on a white image and then examining the pixels to see where you hit and thus how big the largest glyph in your text string was.
Wasteful though that last option sounds, it's probably the quickest and easiest to implement and it - depending on your needs - may actually be the best option all around.
I have a sample pdf (attached), and it includes a text object and a rectangle object that have almost the same height.
Indeed, your PDF is displayed like this:
But looking at this one quickly realizes that the glyphs in your text "Hello World" do not extend beneath the base line like a 'g', 'j' or some other glyphs would:
(The base line is the line through the glyph origins)
Since the two values are not close, my problem is how to get the height/size of the font
Obviously the space required for such descenders beneath the base line must also be part of the font size.
Thus, it is completely correct and not a problem that the height of the box (18.12) is considerably smaller than the font size (24.63024).
BTW, this corresponds with the specification which describes a font size of 1 to be arranged so that the nominal height of tightly spaced lines of text is 1 unit, cf. section 9.2.2 "Basics of Showing Text" of ISO 32000-1. Tightly spaced lines obviously need to include not only glyph parts above the base line but also those below. Additionally it furthermore includes a small gap between such lines as even tightly spaced lines are not expected to touch each other.

Want to set label value on TeeChart InnerTick VC++

I wanted to set label text value on TeeChart InnerTick using VC++. like we have below example (I wanted to set AAAA,BBBB,CCCC,DDDD values).
// Sorry i was not able to attach image.
Below mention chart we have created using CGraph. to get co-ordinates we are using SDKInfo property. i wanted to know that how can we get all these x axis,y axis values using TeeChart. Is there any API to get these co ordinates ?
1
X axis max (your data units)
2
X axis min (your data units)
3
Y axis max (your data units)
4
Y axis min (your data units)
5
X axis length (Graphics Server view units)
6
Y axis length (Graphics Server view units)
7
X origin (Graphics Server view units)
8
Y origin (Graphics Server view units)
9
Label font size (percentage of system font)
|
|
| AAAA BBBB CCC DDDD
|__________|____________________|___________________|__________________|________
| | | |
111 222 333 444
Thanks,
I'm not sure about what exact TeeChart version are you using so this answer may be a bit vague.
All binary installations of TeeChart come with a program we call "Features Demo". This program includes examples showing how to use the majority of features supported.
One of the examples is the "Custom Labels", you should find it under "All Features\Welcome !\Axes\Labels\Custom Labels" in the program.
Also, to convert axis values to screen pixels, all TeeChart versions provide "Calc*" functions for it. Ie, to convert the value 10 in the bottom axis to pixels, in TeeChart ActiveX:
XPix = tChart1.Axis.Bottom.CalcXPosValue(10);
Just note these functions need the chart to be drawn once so the internal properties have been initialized and the calculations can be correctly done.

Resources