2

I gone through different solutions on CTM matrix calculations(someof them are this and this).

What I know about content stream is when "q " encounters we need to push identity matrix in a graphics_stack and keep multiply with next position operator(cm , Tm, Td, TD) CTM. When "Q" encounters we need to pop the last matrix.

For text positioning parsing when "BT" encounters push in identity matrix in position_stack and keep multiply with next position operator(cm , Tm, Td, TD) CTM. When "ET" encounters we need to pop the last matrix.

Here the some times we need to multiply with last CTM matrix and some times just multiply with identity matrix. When these cases are occurs?

Case 1:

enter image description here

From as shown in image 1 and 2 case merely replacement of new matrix from Td to Tm. And from 2 to 3 it's again last CTM multiplication. How I know?(By visually I can tell by looks)

Case 2:

enter image description here

In this case which how the matrix pushing and calculation will be there ?

Case 3:

BT
TT_1 20 Tf
35.56 150.24533 Td _______________ 1
(some sample text) Tj
50.526 250.36 Td  ________________ 2
(second line new replace) Tj
0 -16.2 Td   _____________________ 3
(Line end.) Tj
ET

This case 1 and 2 are merely replacement, 2 and 3 previous matrix multiplication. How do I know?

Case 4:

Please parse these positions at least till 10. The source file of this pdf

Case 5:

enter image description here

In above one need to calculate the l position. I highlighted with 1 to 4 numbers. I need to calculate the positions of each and every l How can do that? pdf

case6:

What is the change in calculation when the page(pdf) is rotated to 90 or 180 or 270 and 315 degrees?

These are some cases what I saw. What else might cases I can encounter and What is the generic approach to solve this ?

  • 1
    I think you mix two different details. You have the current transformation matrix and you have the text and text line matrices. The CTM is subject to **cm**, **q**, and **Q**. The text and text line matrices are subject to **BT**, **Tm**, **Td**, ... And for the exact text position you need the product of text matrix and CTM at the time that text is drawn. If nobody answers in detail before, I'll try and demonstrate that in an answer next week in office. – mkl Jan 03 '20 at 10:35
  • Yes this is both for graphics and text state. Thanks for reply. – fascinating coder Jan 03 '20 at 10:57
  • Any Update. Please. – fascinating coder Jan 07 '20 at 10:58
  • Concerning your case 5: What exactly is the problem here? As far as I can see there is only a single **cm** instruction in place which scales the coordinates down by a factor of 0.1. Thus, all you have to do is divide by 10... – mkl Jan 16 '20 at 13:04
  • I added a discussion of example 5 to my answer. But you probably should finish this question and start a new one for new problems. In particular because example 5 is not about the original topic of this question anymore which was how to combine information from multiple instructions manipulating the multiple matrices concerned. – mkl Jan 16 '20 at 13:46
  • Concerning your newly added case 6: Until now the implied assumption was that you wanted to calculate the matrices to derive coordinates in the default user space coordinate system. If that assumption still holds, then the page rotation value does not change anything as the default user space coordinate system is not influenced by that. Probably you want a different coordinate system, though. In that case, the page rotation value might imply a different CTM starting value than the identity matrix. Thus, please clarify – mkl Jan 30 '20 at 11:15

2 Answers2

3

Operators of interest

First of all, I get the impression you mix up two different aspects. You have the current transformation matrix (CTM) and you have the text and text line matrices. The CTM is subject to cm, q, and Q. The text and text line matrices are subject to BT, Tm, Td, ... And to determine the exact position and direction of drawn text you need the product of text matrix and CTM at the time that text is drawn.

How those operators change the matrices, is described in the PDF specification, ISO 32000 part 1 or 2.

From ISO 32000-1 Table 57 – Graphics State Operators –

  • cm: Modify the current transformation matrix (CTM) by concatenating the specified matrix
  • q: Save the current graphics state including the CTM on the graphics state stack
  • Q: Restore the graphics state including the CTM by removing the most recently saved state from the stack and making it the current state

From ISO 32000-1 Table 107 – Text object operators –

  • BT: Begin a text object, initializing the text matrix, Tm, and the text line matrix, Tlm, to the identity matrix.

From ISO 32000-1 Table 108 – Text-positioning operators –

  • tx ty Td: Move to the start of the next line, offset from the start of the current line by (tx, ty). More precisely, this operator shall perform these assignments:

  • a b c d e f Tm: Set the text matrix, Tm, and the text line matrix, Tlm:

Furthermore, TD, T*, ', and " operate on Tm and Tlm in a way specified using the Td operator.

From ISO 32000-1 section 9.4.4 – Text Space Details –

Whenever a glyph is drawn, its entire transformation from text space may be represented by a text rendering matrix, Trm:

where Tfs is the current font size, Th is the current horizontal scaling factor, and Trise is the current text rise value.

After drawing that glyph, Tm is updated according to the glyph displacement

In horizontal mode tx is the displacement and ty is zero, in vertical mode tx is zero and ty is the displacement. The applicable value is calculated as

Example 1

In the following paragraphs I use rounded values to concentrate on the essentials.

The CTM starts as the Identity matrix, and as there is no cm operation here, it remains so all the time. Tm and Tlm on the other hand do change:

BT

Tm and Tlm are both set to the identity matrix

/GS0 gs
/T1_0 10 Tf

No change to Tm or Tlm.

317 65 Td

This multiplies a translation matrix as described above from the left to the former value of the Tlm and sets Tm and Tlm to the result:

             1  0  0     1 0 0       1  0  0
T  = T   =   0  1  0  *  0 1 0  =    0  1  0
 m    lm   317 65  1     0 0 1     317 65  1

As that former value was the identity, this may look like a replacement by the Td translation matrix but it actually is a multiplication.

(F)Tj

This draws a glyph transformed by the text rendering matrix

       10 × 1   0  0       1  0  0     1 0 0      10  0  0
T   =       0  10  0  *    0  1  0  *  0 1 0  =    0 10  0
 rm         0   0  1     317 65  1     0 0 1     317 65  1

Thereafter Tm is updated as described above. Unfortunately we don't have the widths of the font T1_0, so we cannot calculate the updated value.

1 0 0 1 370 87 Tm

This sets the text matrix, Tm, and the text line matrix, Tlm, to the given matrix:

              1  0  0
T  = T   =    0  1  0
 m    lm    370 87  1

So now we know the current Tm value again.

-47 -22 Td

This multiplies a translation matrix as described above from the left to the former value of the Tlm and sets Tm and Tlm to the result:

              1   0  0       1  0  0       1  0  0
T  = T   =    0   1  0  *    0  1  0  =    0  1  0
 m    lm    -47 -22  1     370 87  1     323 65  1
[(igure)-251(2.3:)-621(P)16...] TJ

This draws the strings in the argument array with a starting text rendering matrix of

       10 × 1   0  0       1  0  0     1 0 0      10  0  0
T   =       0  10  0  *    0  1  0  *  0 1 0  =    0 10  0
 rm         0   0  1     323 65  1     0 0 1     323 65  1

updating Tm again and again as described above.

Example 2

In the following paragraphs I use rounded values to concentrate on the essentials.

The CTM starts as the Identity matrix.

q

This saves the current graphics state, including the current CTM. As there is no Q operation in the example, though, we can ignore that for now.

.24 0 0 .24 91 740 cm

This updates the CTM by the given matrix:

        0.24   0     0     1 0 0      0.24   0     0
CTM =   0      0.24  0  *  0 1 0  =   0      0.24  0
       91    740     1     0 0 1     91    740     1
BT

Tm and Tlm are both set to the identity matrix

133 0 0 133 0 0 Tm

This sets the text matrix, Tm, and the text line matrix, Tlm, to the given matrix:

            133   0  0
T  = T   =    0 133  0
 m    lm      0   0  1
/TT1.0 1 Tf
.002 Tc

No change to CTM, Tm, or Tlm.

[(The)1( )1(Long )1(Tai)1(l)]TJ

This draws the strings in the argument array with a starting text rendering matrix of

       1 × 1  0  0     133   0  0      0.24   0    0     32   0  0
T   =      0  1  0  *    0 133  0  *   0      0.24 0  =   0  32  0
 rm        0  0  1       0   0  1     91    740    1     91 740  1

updating Tm again and again as described above.

Example 3

BT
TT_1 20 Tf
35.56 150.24533 Td _______________ 1
(some sample text) Tj
50.526 250.36 Td  ________________ 2
(second line new replace) Tj
0 -16.2 Td   _____________________ 3
(Line end.) Tj
ET

In the following paragraphs I use rounded values to concentrate on the essentials.

The CTM starts as the Identity matrix, and as there is no cm operation here, it remains so all the time. Tm and Tlm on the other hand do change:

BT

Tm and Tlm are both set to the identity matrix

TT_1 20 Tf

No change to Tm or Tlm.

36 150 Td

This multiplies a translation matrix as described above from the left to the former value of the Tlm and sets Tm and Tlm to the result:

            1   0  0     1 0 0      1   0  0
T  = T   =  0   1  0  *  0 1 0  =   0   1  0
 m    lm   36 150  1     0 0 1     36 150  1

As that former value was the identity, this may look like a replacement by the Td translation matrix but it actually is a multiplication.

(some sample text) Tj

This draws glyphs transformed by the text rendering matrix

       20 × 1   0  0      1   0  0     1 0 0     20   0  0
T   =       0  20  0  *   0   1  0  *  0 1 0  =   0  20  0
 rm         0   0  1     36 150  1     0 0 1     36 150  1

Thereafter Tm is updated as described above. Unfortunately we don't have the widths of the font TT_1, so we cannot calculate the updated value.

51 250 Td

This multiplies a translation matrix as described above from the left to the former value of the Tlm and sets Tm and Tlm to the result:

            1   0  0      1   0  0      1   0  0
T  = T   =  0   1  0  *   0   1  0  =   0   1  0
 m    lm   51 250  1     36 150  1     87 400  1

So now we know the current Tm value again.

(second line new replace) Tj

This draws glyphs transformed by the text rendering matrix

       20 × 1   0  0      1   0  0     1 0 0     20   0  0
T   =       0  20  0  *   0   1  0  *  0 1 0  =   0  20  0
 rm         0   0  1     87 400  1     0 0 1     87 400  1

Thereafter Tm is updated as described above. Unfortunately we don't have the widths of the font TT_1, so we cannot calculate the updated value.

0 -16 Td

This multiplies a translation matrix as described above from the left to the former value of the Tlm and sets Tm and Tlm to the result:

            1   0  0      1   0  0      1   0  0
T  = T   =  0   1  0  *   0   1  0  =   0   1  0
 m    lm    0 -16  1     87 400  1     87 384  1

So now we know the current Tm value again.

(Line end.) Tj

This draws glyphs transformed by the text rendering matrix

       20 × 1   0  0      1   0  0     1 0 0     20   0  0
T   =       0  20  0  *   0   1  0  *  0 1 0  =   0  20  0
 rm         0   0  1     87 384  1     0 0 1     87 384  1

Thereafter Tm is updated as described above. Unfortunately we don't have the widths of the font TT_1, so we cannot calculate the updated value.

Example 4

We discussed example 4 in your answer and the comments to it.

Example 5

q 0.1 0 0 0.1 0 0 cm
/R108 gs
0 g
q
...
Q
0 0 1 rg
q
...
Q
4.05 w
0 G
722.023 4082.13 m
722.023 4490.28 l
S
723.961 4488.25 m
2872.98 4488.25 l
S
404.1 w
0 0 0.199951 0 K
723.961 4284.18 m
2872.98 4284.18 l
S
4.05 w
0 G
720 4080.2 m
2876.94 4080.2 l
S
2874.91 4082.13 m
2874.91 4490.28 l
S
0 g
q 

Why does your view of those instructions show the numbers inaccurately? The above is copy&pasted from the stream contents, there is no need to change the numbers like that...

q
0.1 0 0 0.1 0 0 cm

Sets the CTM to

       0.1 0   0
CTM =  0   0.1 0
       0   0   1
/R108 gs
...
0 G

Nothing happens to the CTM

722.023 4082.13 m
722.023 4490.28 l
S

We have to apply the CTM to these coordinates

                      0.1 0   0
[722.023 4082.13]  *  0   0.1 0  = [72.2023 408.213]
                      0   0   1

                      0.1 0   0
[722.023 4490.28]  *  0   0.1 0  = [72.2023 449.028]
                      0   0   1

Thus, a line is stroked from 72.2023,408.213 to 72.2023,449.028.

723.961 4488.25 m
2872.98 4488.25 l
S

Just like above, a line is drawn from 72.3961,448.825 to 287.298,448.825.

404.1 w
0 0 0.199951 0 K
723.961 4284.18 m
2872.98 4284.18 l
S

And again, a line is drawn from 72.3961,428.418 to 287.298,428.418. The only notable thing here is that the line is quite wide, ca. 40 units, so this "line" actually looks more like a filled rectangle and represents the background of the text box with the Lorentz force characterization.

4.05 w
0 G
720 4080.2 m
2876.94 4080.2 l
S

Another line is drawn, narrow again and, therefore, looking like a line, this time from 72,408.02 to 287.694 408.02.

2874.91 4082.13 m
2874.91 4490.28 l
S

And finally the last line, this time from 287.491,408.213 to 287.491,449.028.

Community
  • 1
  • 1
mkl
  • 90,588
  • 15
  • 125
  • 265
  • Thanks for your reply. As per example 1, when td is encountered, we are multiplying with previous Tm matrix but in example 3, if i follow that approach, i dont get the desired position. BT TT_1 20 Tf 35.56 150.24533 Td _______________ 1 (some sample text) Tj 50.526 250.36 Td ________________ 2 (Here it should multiply with identity matrix) (second line new replace) Tj 0 -16.2 Td _____________________ 3 (Here it should multiply with last Tm) (Line end.) Tj ET When should it multiply with identity and when should we multiply it with last Tm ? – fascinating coder Jan 08 '20 at 10:23
  • @fascinatingcoder I added a discussion of your example 3. All **Td** operations work by multiplication with the former text line matrix value. If your PDF displays differently, other instructions must also be involved and I need the file for analysis to tell you which, so in that case please share it. – mkl Jan 08 '20 at 15:01
  • EX3: Question is from 1 Td to 2 Td we have to multiply with identity Text matrix but 2 Td to 3 Td multiplied with last text matrix. How we now that? I am searching for the file where I got this scenario. I will upload once I found. – fascinating coder Jan 09 '20 at 07:55
  • For **Td** you *always* have to multiply with the *text line matrix* at that time (not the text matrix). If the text line matrix at that time is the identity matrix, this means multiplying with the identity matrix. If it's not the identity matrix, this means not multiplying with the identity matrix. – mkl Jan 09 '20 at 12:07
  • Could you please try to add solution for case4. I updated in question(added source file also). – fascinating coder Jan 13 '20 at 06:55
  • I'll try and do so later. But essentially it's the same procedure as before, here merely one has to explicitly keep track of the graphics state stack as there are numerous **q** and **Q** operations and intermingled **cm** operations. – mkl Jan 13 '20 at 07:41
  • You can edit my below answer. That might be save your time. Thanks for reply. – fascinating coder Jan 13 '20 at 08:03
  • As proposed we discuss example 4 in [your answer](https://stackoverflow.com/a/59712497/1729265) and the comments to it. – mkl Jan 13 '20 at 13:06
0

Example 4 solution:

enter image description here

The CTM starts as the Identity matrix.

q

This saves the current graphics state (Graphics state = Identity ---- 1).

1 0 0 1 62.692 277.671 cm

This updates the CTM by the given matrix:

        1      0      0     1 0 0      1      0      0
CTM =   0      0      0  *  0 1 0  =   0      1      0    ----- 2
        62.692 277.67 1     0 0 1     62.692  277.67 1

CTM is updated to above result

q

push the CTM save to graphics state(Graphics state = 1, 2)

q

Again push the same CTM matrix to graphics state(Graphics state = 1, 2, 2)

1 0 0 1 286.59 207.54 cm

Update the CTM by current matrix.

      1      0      0   1      0      0        1      0      0
CTM = 0      1      0 * 0      0      0    =   0      1      0    ----- 3
      286.49 207.54 1  62.692 277.67 1        349.18 485.21  1

CTM updated to above result matrix.

q

push the CTM save to graphics state(Graphics state = 1, 2,2,3)

.75 .75 .75 RG
n
11.33 19.84 171.67 232.146 re
S

This will nothing change in position matrices.

Q

Assign the CTM to last available graphics state CTM = 3. Q will remove the last graphics state from graphics stack.(Graphics state = 1, 2,2)

1 0 0 1 17.007 23.52 cm 

Update the CTM by current matrix.

      1      0      0   0      1      0        0      1      0
CTM = 0      1      0 * 0      0      0    =   0      1      0    ----- 4
      17.007 23.52  1  349.18 485.21  1        366.18 508.73 1

CTM updated to above result matrix.

q

push the last CTM to graphics state(Graphics state = 1, 2,2,4) Skip rg, RG

BT

assign Tm and Tlm to Identity matrix.

1 0 0 1 0 5.6 Tm

This sets the text matrix, Tm, and the text line matrix, Tlm, to the given matrix:

              1   0    0
T  = T   =    0   1    0
 m    lm      0   5.6  1

Tm and Tlm updated.

46.22 0 Td

Translate the matrix according to above Td matrix.

             1   0  0     1 0   0       1     0  0
T  = T   =   0   1  0  *  0 1   0  =    0     1  0
 m    lm   46.22 0  1     0 5.6 1       46.22 5.6  1

update the Tm and Tlm with above matrix. Next Tf you can leave.

12 TL

This sets the graphics state leading text parameter, no direct influence on Tm or Tlm.

(William Shakespeare) Tj

This draws the string glyph-by-glyph, the first glyph transformed by this text rendering matrix

       8 × 1   0  0       1    0   0     1     0      0      8     0       0
T   =       0  8  0  *    0    1   0  *  0     1      0  =   0     8       0
 rm         0  0  1     46.22 5.6  1    366.18 508.73 1     412.4 514.33  1

While the string is rendered glyph-by-glyph, Tm is updated as described in ISO 32000-1 section 9.4.4, and so is the Trm.

T*

As the text leading currently is 12, T* is equivalent to 0 -12 Td, so:

             1   0  0     1     0   0       1       0   0
T  = T   =   0   1  0  *  0     1   0  =    0       1   0
 m    lm     0  -12 1     46.22 5.6 1       46.22 -6.4  1
mkl
  • 90,588
  • 15
  • 125
  • 265
  • @ mkl Above my answer is wrong calculation, according to the pdf text positions. The William Shakespeare exact positions 412.487 502.211 but in above calculation it is wrong. Please make it correct. Thanks. – fascinating coder Jan 13 '20 at 08:01
  • First error: You process the first **Q** like this: *"Q will remove the last graphics state from graphics stack.(Graphics state = 1, 2,2) And assign the CTM to last available graphics state. CTM = 2 no matrix."* But the CTMs on the graphics state stack were 1,2,2,3 before, so the state you restore here now has a current CTM #3, not #2. Thus, the CTM value you calculate as #4 also is wrong (missing the shift by 286, 207) and consequentially also the starting Trm for "William Shakespeare". – mkl Jan 13 '20 at 10:55
  • Second error: You process the 12 **TL** like this: *"Mostly it will translate like T\*"* No, it does not translate at all, it merely sets the *Leading* value. Thus, you change the Tm and Tlm too early, adding to the Trm incorrectness due to the first error. – mkl Jan 13 '20 at 11:02
  • **T\*** *"We did calculation for this T\* while TL encountered."* But why? When **TL** is set, its value may or may not be used later. If there is no following **T\*** or **'** operator, the value **TL** sets will make no difference. – mkl Jan 13 '20 at 11:05
  • As an aside, there are invalid operations in that content stream: multiple times there is a **n** instruction *before* a path. This is not allowed, **n** is a path-painting operator (it's the operator that neither strokes nor fills) and, therefore, may only be used *after* a path definition. – mkl Jan 13 '20 at 11:12
  • Third error: You describe that you want to process 46.22 0 **Td** like this *"Translate the matrix according to above Td matrix."* but in your calculation you don't apply the translation to the previous Tlm matrix but instead to the identity matrix, adding to the Trm incorrectness due to the former errors. – mkl Jan 13 '20 at 11:27
  • *"The William Shakespeare exact positions 412.487 502.211"* - At least your "exact" y coordinate is false. The bottom line of the stroked rectangle around image and name is at y = 277.67... + 207.5... + 19.8... = ca. 505, so the y coordinate of the text baseline must be a bit larger. By using the cursor coordinate shown in Adobe Acrobat I get a value of about 514. – mkl Jan 13 '20 at 11:38
  • After your [second edit](https://stackoverflow.com/revisions/59712497/3): 12 **TL** *"Text Leading is 12 Because its y axis. 0, -12 Td Will work here."* No. **TL** merely sets a text state value for leading but it does not do any change to any matrix. Where in the specification did you read that **TL** by itself does change the Tlm or Tm? – mkl Jan 13 '20 at 11:45
  • Could you please tell here what's going on when TL encountered. – fascinating coder Jan 13 '20 at 11:50
  • *"what's going on when TL encountered"* - According to the PDF specification: "*leading* **TL** Set the text leading, Tl, to *leading*, which shall be a number expressed in unscaled text space units." That's it, merely the text state leading value in the current graphics state is set. This value does not influence the Tm or Tlm before and unless a **T\*** or **'** is encountered. – mkl Jan 13 '20 at 11:55
  • can you please continue the calculation what will happens when T* encountered here. Generally what I will do when T* encounters I will take the Last tx, ty of TD and I will do the matrix calculation same like tx ty TD. Is it correct? – fascinating coder Jan 13 '20 at 12:25
  • *"what will happens when T* encountered here"* Essentially what you originally did too early while processing **TL**. Tm and Tlm are changed, but they are changed *after* drawing the text "William Shakespeare", so this change does *not* influence the position of that text. – mkl Jan 13 '20 at 12:34
  • *"Generally what I will do when T\* encounters I will take the Last tx, ty of TD and I will do the matrix calculation same like tx ty TD. Is it correct?"* No, Look up the specification, "**T\*** Move to the start of the next line. This operator has the same effect as the code 0 -Tl **Td** where Tl denotes the current leading parameter in the text state." That Tl value may either be set by **TL** or by **TD**. – mkl Jan 13 '20 at 12:43
  • BTW, **TL** is a *text state operator*, not a *text positioning operator*. Thus, it can be used not only in text objects but also at page description level. Unless overridden later, therefore, a **TL** operation at the start of the content stream may still influence the text drawing in the last text object of that content stream (or even in a later content stream in the same content stream array of a page). Also it is subject to **q** and **Q**. Thus, while we here only looked at the CTM on the graphics states stack, you have to keep track of other properties there, too, leading, font, size... – mkl Jan 13 '20 at 13:03