Skip to content

Form XObject text geometry ignores nested transformation matrices #89

Description

@kari-pikkarainen

Bug

Text extracted from some native-text Form XObjects is decoded correctly but is emitted with overlapping origin-based geometry because the text extractor does not compose the page cm, Form /Matrix, and nested glyph cm transformations.

This pattern is produced by PDF generators that render unit-sized glyphs at the text-space origin and supply their actual position through transformation matrices.

Reproduction

With gxpdf v0.9.4, a synthetic one-page PDF containing a positioned financial table produces readable characters through the text extractor, but the glyph coordinates overlap and all four table modes return no tables:

MethodAuto:    0 tables
MethodStream:  0 tables
MethodLattice: 0 tables
MethodHybrid:  0 tables

The reproduction composes:

  1. a page-level scale through cm;
  2. a Form XObject /Matrix translation; and
  3. a separate scale/translation cm for each unit-sized glyph.

It also includes direct-page text after the Form to verify that graphics state is restored.

Expected behavior

ExtractTextElements() should return finite page-space positions after composing those matrices, without leaking Form graphics state into the caller. Because ExtractTables() consumes the same text elements, table detection should receive the corrected geometry as well.

Proposed fix

  • track q, Q, and cm in the text extractor;
  • apply Form /Matrix while processing Form XObjects;
  • isolate each Form's graphics-state stack from its caller;
  • use embedded simple/composite font widths for individually positioned glyph advances;
  • bound composite /W ranges to the valid unsigned 16-bit CID space.

The initial coordinate path is deliberately limited to unit-sized text inside Form XObjects to avoid silently changing the established coordinate space of larger Form text. Removing that compatibility threshold requires coordinated re-baselining of text geometry and table-coordinate normalization.

Scope note

Corrected geometry makes the synthetic table detectable, but existing Stream column-boundary behavior can still omit a far-right financial column. That is a separate table-reconstruction issue and should not be presented as solved by this geometry change.

Environment

  • gxpdf: v0.9.4
  • Go: 1.26.6
  • macOS arm64; the reproduction is deterministic and platform-independent

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions