# `PdfElixide.Document.Path`
[🔗](https://github.com/r8/pdf_elixide/blob/v0.16.0/lib/pdf_elixide/document/path.ex#L1)

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`. Extraction normalizes every colorspace
to DeviceRGB, 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
`PdfElixide.Document.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.

# `operation`

```elixir
@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`

```elixir
@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()
}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
