title: Parse PDF text position with graphics state clipping path declared
body: <p>Pardon the long posting, but whomever might answer this question will undoubtedly need all the information provided.</p>
  
  <p>I have a somewhat successful implementation of PDF parsing for a project I'm working on. In chasing down an issue in exact text positioning in a particular PDF, I found my calculation to be wrong. Here are the relevant snippets of the PDF (using <code>qpdf -qdf</code>):</p>
  
  <p><strong>Relevant PDF snippets</strong></p>
  
  <pre><code>%% Page 1
  8 0 obj
  &lt;&lt;
    /Contents [
      10 0 R
      12 0 R
    ]
    /MediaBox [ 0 0 612 792 ]
    /Type /Page
  &gt;&gt;
  endobj
  </code></pre>
  
  <p>=====</p>
  
  <pre><code>9 0 obj
  &lt;&lt;
    /MediaBox [ 0 0 595 842 ]
    /Type /Pages
  &gt;&gt;
  endobj
  </code></pre>
  
  <p>======</p>
  
  <pre><code>%% Contents for page 1
  10 0 obj
  &lt;&lt;
    /Length 11 0 R
  &gt;&gt;
  stream
  q
  .94062 0 0 .94062 26.16627 0 cm
  0 0 m
  595 0 l
  595 842 l
  0 842 l
  0 0 l
  h
  W n
  q
  16.84 16.84 561.32 808.32 re
  W n
  q
  .24 0 0 .24 90.75993 740.44 cm
  BT
  133 0 0 133 0 0 Tm /TT1.0 1 Tf .0017 Tc [(The)1( )1(Long )1(Tai)1(l)]TJ
  ET
  </code></pre>
  
  <p>I've removed extraneous portions of each snippet.</p>
  
  <p>The following details my calculation of the exact PDF rect for the <strong>T</strong> in the first word on page 1.</p>
  
  <h2>Descriptions and effects of ops in stream of last PDF snippet above</h2>
  
  <p><strong>q</strong> : Push graphics state (GS).</p>
  
  <pre><code>   ctm = identity
  </code></pre>
  
  <p><strong>cm</strong> : Modify ctm</p>
  
  <pre><code>         |  0.94062   0        0 |
     ctm = |  0         0.94062  0 |
           | 26.16627   0        1 |
  </code></pre>
  
  <p><strong>m</strong>  : Start path at <strong>0, 0</strong></p>
  
  <p><strong>l</strong>  : Line to <strong>595, 0</strong></p>
  
  <p><strong>l</strong>  : Line to <strong>595, 842</strong></p>
  
  <p><strong>l</strong>  : Line to <strong>0, 842</strong></p>
  
  <p><strong>l</strong>  : Line to <strong>0, 0</strong></p>
  
  <p><strong>h</strong>  : Close path</p>
  
  <pre><code>   No op since path already closed
  </code></pre>
  
  <p><strong>n</strong>  : Intersect GS clipping path</p>
  
  <pre><code>   GS clipping path wasn't set, so GS clipping path is the closed path that
     defines rect [0 0 595 842]
  </code></pre>
  
  <p><strong>q</strong> : Push GS</p>
  
  <p><strong>re</strong> : Create closed path</p>
  
  <pre><code>  Path defines rect [16.84 16.84 561.32 808.32]
  </code></pre>
  
  <p><strong>n</strong> : Intersect GS clipping path</p>
  
  <pre><code>   The incoming path is totally contained in current GS clipping path,
     so the GS clipping path becomes the incoming path, which defines the rect
     [16.84 16.84 561.32 808.32]
  </code></pre>
  
  <p><strong>q</strong> : Push GS</p>
  
  <p><strong>cm</strong> : Modify ctm</p>
  
  <pre><code>         |  0.941   0      0 |   | 0.24     0    0 |   | 0.226   0     0 |
     ctm = |   0      0.941  0 | * | 0        0.24 0 | = | 0       0.226 0 |
           |  26.167  0      1 |   | 90.756 740.44 1 |   | 97.04 740.44  1 |
  </code></pre>
  
  <p><strong>BT</strong> : Begin text object</p>
  
  <p><strong>Tm</strong> : Set text and line matrices</p>
  
  <pre><code>             | 133   0 0 |
     tm = lm = |   0 133 0 |
               |   0   0 1 |
  </code></pre>
  
  <p><strong>Tf</strong> : Set text font and font size</p>
  
  <pre><code>   Set font to TT1, which has ascent=750, descent=-250, and glyph width T=667
     (I will only need the width of T below). Font size is set to 1.
  
     This has the effect of setting the first component in calculating the text
     rendering matrix to
  
        | 1   0    0 |
        | 0   1    0 |
        | 0  -0.25 1 |
  </code></pre>
  
  <p><strong>Tc</strong> : Set text char spacing</p>
  
  <p><strong>TJ</strong> : Show text</p>
  
  <h2>Caculation of PDF rect for T</h2>
  
  <p>Our target text for calculations will be the first text for display, the letter
  <strong>T</strong> (hence the only glyph width provided above). To perform the text showing
  operator <strong>TJ</strong>, we first calculate <strong>Trm</strong> as specified in section <strong>9.4.4</strong> of PDF spec <strong>(PDF 32000-1:2008)</strong> :</p>
  
  <pre><code>  a = Tfs * Th = 1 * 1 = 1
    d = Tfs = 1
    ty = Trise = -250/1000 = -0.25
  
           |  a   0  0 |   
     Trm = |  0   d  0 | * Tm * ctm
           |  0  ty  1 |   
  
           | 1   0    0 |   | 133   0  0 |   | 0.226   0     0 |
     Trm = | 0   1    0 | * |   0 133  0 | * | 0       0.226 0 |
           | 0  -0.25 1 |   |   0   0  1 |   | 97.04 740.44  1 |
  
           | 30.06   0     0 |
         = |  0     30.06  0 |
           | 97.04 732.93  1 |
  </code></pre>
  
  <p>The glyph width (horizontal displacement) and glyph height are also calculated
  as per section <strong>9.4.4</strong>:</p>
  
  <pre><code>  width  = width of 'T' / 1000 = 0.667
    height = (ascent - descent) / 1000 = (750 + 250) / 1000 = 1
  </code></pre>
  
  <p>The text rect for the letter <strong>T</strong> is:</p>
  
  <pre><code>  textRect for 'T' = [ 0 decent/1000, width, height ]
                     = [ 0 -0.25, 0.667, 1 ]
  </code></pre>
  
  <p>The <strong>T</strong> is rendered at the text rect transformed by the <strong>ctm</strong>. Calculating the resulting PDF rect for <strong>T</strong>:  </p>
  
  <p>PDF rect :   <code>97.04  732.93  20.03  30.02</code></p>
  
  <p>I have created highlight annotations for the <strong>T</strong> in both <em>Acrobat</em> and <em>Preview</em>
  and used that information to determine the calculation of the PDF position of
  <strong>T</strong> in both those programs as:</p>
  
  <p><em>Acrobat</em> :  <code>111.54  718.99  20.03  30.02</code></p>
  
  <p><em>Preview</em> :  <code>111.54  718.99  20.03  30.02</code></p>
  
  <p>Compared to <em>Acrobat</em> and <em>Preview</em>, I have <strong>dx = -14.5</strong> and <strong>dy = 13.31</strong>, that is, I'm
  too far left and too high of the actual position of the <strong>T</strong>.</p>
  
  <p>For PDFs that do not have a changed Media Box or do not declare a graphics
  state clipping path all my calculations are spot on. I know it must be related
  to either the differing Media Box declarations in PDF objects <strong>8 0</strong> and <strong>9 0</strong> or,
  more probable, the graphics state clipping path resulting from the <strong>m, l, h, n,</strong>
  and <strong>re</strong> operators, which resulted in a rect clipping path rect of </p>
  
  <pre><code>[ 16.84 16.84 561.32 808.32 ]
  </code></pre>
  
  <p>while the Media Box was</p>
  
  <pre><code>[ 0 0 595 842 ]
  </code></pre>
  
  <p>I can't find any section in the PDF spec that indicates a change in my calculations due to the graphics state clipping path (again, assuming that
  is the culprit).</p>
  
  <p>Ugh. What am I missing?</p>