PdfElixide.Document.Path (pdf_elixide v0.12.0)

Copy Markdown View Source

A vector graphics path — a line, curve, rectangle, or filled shape — extracted from a PDF page, with its zero-based page index and bounding box.

The :operations field holds the path's drawing commands as a list of flat tagged tuples, in stream order:

  • {:move_to, x, y} — start a new subpath at (x, y)
  • {:line_to, x, y} — straight line to (x, y)
  • {:curve_to, c1x, c1y, c2x, c2y, ex, ey} — cubic Bézier curve (the two control points and the endpoint)
  • {:rectangle, x, y, width, height} — a complete rectangle subpath
  • :close_path — close the current subpath

A path may be stroked, filled, or both: :stroke_color and :fill_color are each a PdfElixide.Color.RGB or nil. Upstream resolves every colorspace to DeviceRGB during extraction, so these are always RGB — never the other PdfElixide.Color structs.

Rectangles and straight lines

PdfElixide.Document.rects/2 and PdfElixide.Document.lines/2 return these same structs, narrowed by a test of :operations alone — of the drawing commands, not of the geometry they describe:

  • a rectangle is a lone {:rectangle, …}, or a {:move_to, …} followed by exactly three {:line_to, …}, optionally then :close_path;
  • a straight line is a {:move_to, …} followed by exactly one {:line_to, …}, optionally then :close_path.

(Those are vector graphics; PdfElixide.Document.text_lines/2 is the unrelated text extractor.) The two sets are therefore disjoint subsets of paths/2, with four consequences:

  • A rectangle drawn back to its starting corner is classified as neither — one {:line_to, …} too many. Boxes drawn with the re operator, the usual case, are unaffected.
  • Four points on one straight line are classified as a rectangle, and arrive with a zero-height or zero-width :bbox.
  • Degenerate shapes are kept, so filter on :bbox if you need shapes with extent.
  • A curve disqualifies a path entirely. A rounded rectangle is neither.

Each call runs a full extraction of the page, so asking one page for both its rectangles and its lines costs twice what asking for either does.

One shape reaches none of the three: a path painted with B, B* or b* (fill and stroke together) is not extracted at all, and its commands are prepended to the next path — changing that path's operation count, so a rectangle or line drawn right after one goes missing too.

Summary

Types

A single drawing command in a path's :operations list.

t()

Types

operation()

@type operation() ::
  {:move_to, float(), float()}
  | {:line_to, float(), float()}
  | {:curve_to, float(), float(), float(), float(), float(), float()}
  | {:rectangle, float(), float(), float(), float()}
  | :close_path

A single drawing command in a path's :operations list.

t()

@type t() :: %PdfElixide.Document.Path{
  bbox: PdfElixide.Geometry.Rect.t(),
  dash_pattern: {[float()], float()} | nil,
  fill_color: PdfElixide.Color.RGB.t() | nil,
  layer: String.t() | nil,
  line_cap: :butt | :round | :square,
  line_join: :miter | :round | :bevel,
  operations: [operation()],
  page: non_neg_integer(),
  stroke_color: PdfElixide.Color.RGB.t() | nil,
  stroke_width: float()
}