Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Min row height in tables + docs on fpdf2 internals #1346

Merged
merged 2 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
## [2.8.3] - Not released yet
### Added
* support for [shading patterns (gradients)](https://py-pdf.github.io/fpdf2/Patterns.html)
* support for strikethrough text
* Improved SVG image speed by 50% to 70% - thanks to @petri-lipponen-movesense - [PR #1350](https://github.com/py-pdf/fpdf2/pull/1350)
* improved SVG image speed by 50% to 70% - thanks to @petri-lipponen-movesense - [PR #1350](https://github.com/py-pdf/fpdf2/pull/1350)
* support for <s>strikethrough text</s>
* support for [setting a minimal row height in tables](https://py-pdf.github.io/fpdf2/Tables.html#setting-row-height)
* support for [`v_align` at the row level in tables](https://py-pdf.github.io/fpdf2/Tables.html#setting-vertical-alignment-of-text-in-cells)
* documentation on [verifying provenance of `fpdf2` releases](https://py-pdf.github.io/fpdf2/#verifying-provenance)
* documentation on [`fpdf2` internals](https://py-pdf.github.io/fpdf2/Internals.html)
### Fixed
* [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html): Fixed rendering of content following `<a>` tags; now correctly resets emphasis style post `</a>` tag: hyperlink styling contained within the tag authority. - [Issue #1311](https://github.com/py-pdf/fpdf2/issues/1311)

Expand Down
97 changes: 97 additions & 0 deletions docs/Internals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# fpdf2 internals

## FPDF.pages
`FPDF` is designed to add content progressively to the document generated, page by page.

Each page is an entry in the `.pages` attribute of `FPDF` instances.
Indices start at 1 (the first page) and values are [`PDFPage`](https://py-pdf.github.io/fpdf2/fpdf/output.html#fpdf.output.PDFPage) instances.

`PDFPage` instances have a `.contents` attribute that is a [`bytearray`](https://docs.python.org/3/library/stdtypes.html#bytearray) and contains the **Content Stream** for this page
(`bytearray` makes things [a lot faster](https://github.com/reingart/pyfpdf/pull/164)).

Going back to a previously generated page to add content is possible,
using the [`.page` attribute](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.page), but may result in unexpected behavior, because [.add_page()](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.add_page) takes special care to ensure the page's content stream matches `FPDF`'s instance attributes.


## syntax.py & objects serialization
The [syntax.py](https://github.com/py-pdf/fpdf2/blob/master/fpdf/syntax.py) package contains classes representing core elements of the PDF syntax.

Classes inherit from the [PDFObject](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.PDFObject) class, that has the following properties:

* every PDF object has an `.id`, that is assigned during the document serialization by the [OutputProducer](#outputproducer)
* the `.serialize()` method renders the PDF object as an `obj<</>>endobj` text block. It can be overridden by child classes.
* the `.content_stream()` method must return non empty bytes if the PDF Object has a _content stream_

Other notable core classes are:

* [Name](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.Name)
* [Raw](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.Raw)
* [PDFString](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.PDFString)
* [PDFArray](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.PDFArray)
* [PDFDate](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.PDFDate)


## GraphicsStateMixin
This _mixin_ class, inherited by the `FPDF` class,
allows to manage a stack of graphics state variables:

* docstring: [fpdf.graphics_state.GraphicsStateMixin](https://py-pdf.github.io/fpdf2/fpdf/graphics_state.html#fpdf.graphics_state.GraphicsStateMixin)
* source file: [graphics_state.py](https://github.com/py-pdf/fpdf2/blob/master/fpdf/graphics_state.py)

The main methods of this API are:

* [_push_local_stack()](https://py-pdf.github.io/fpdf2/fpdf/graphics_state.html#fpdf.graphics_state.GraphicsStateMixin._push_local_stack): Push a graphics state on the stack
* [_pop_local_stack()](https://py-pdf.github.io/fpdf2/fpdf/graphics_state.html#fpdf.graphics_state.GraphicsStateMixin._pop_local_stack): Pop the last graphics state on the stack
* [_get_current_graphics_state()](https://py-pdf.github.io/fpdf2/fpdf/graphics_state.html#fpdf.graphics_state.GraphicsStateMixin._get_current_graphics_state): Retrieve the current graphics state
* [_is_current_graphics_state_nested()](https://py-pdf.github.io/fpdf2/fpdf/graphics_state.html#fpdf.graphics_state.GraphicsStateMixin._is_current_graphics_state_nested): Indicate if the stack contains items (else it is empty)

Thanks to this _mixin_, we can use the following semantics:
```python
{% include "../tutorial/graphics_state.py" %}
```

The graphics states used in the code above
can be depicted by this diagram:

``` mermaid
stateDiagram-v2
direction LR
state gs0 {
initial1 : Base state
}
state gs1 {
initial2 : Base state
font_size_pt2 : font_size_pt=16
underline2 : underline=True
font_size_pt2 --> initial2
underline2 --> font_size_pt2
}
gs0 --> gs1: Step 1
state "gs0" as stack2 {
initial3 : Base state
}
gs1 --> stack2: Step 2
```


## OutputProducer
In `fpdf2`, the `FPDF` class is used to store the document **definition**,
its state as it is progressively built. Most attributes and internal data is **mutable**.

Once it's done, when the [FPDF.output()](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.output) method is called, the actual PDF file creation is delegated to the [OutputProducer](https://py-pdf.github.io/fpdf2/fpdf/output.html#fpdf.output.OutputProducer) class.

It performs the serialization of the PDF document,
including the generation of the [cross-reference table & file trailer](https://py-pdf.github.io/fpdf2/fpdf/output.html#fpdf.output.PDFXrefAndTrailer).
This class uses the `FPDF` instance as **immutable input**:
it does not perform any modification on it.

<!-- Other topics to mention:

## Vector Graphics
drawing.py & svg.py packages

## Text regions & flow ?

## Text shaping ?

-->
3 changes: 3 additions & 0 deletions docs/Signing.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ allows to add a signature based on arbitrary key & certificates, not necessarily
[examples/pdf-verify.py](https://github.com/m32/endesive/blob/master/examples/pdf-verify.py)
or the [`check_signature()`](https://github.com/py-pdf/fpdf2/blob/master/test/conftest.py#L111) function
used in `fpdf2` unit tests can be good starting points for you, if you want to perform PDF signature control.

If you want to sign **existing** PDF documents,
you should consider using PyHanko: <https://pyhanko.readthedocs.io>.
25 changes: 19 additions & 6 deletions docs/Tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,24 +120,37 @@ left and right is supplied then c_margin is ignored.

_New in [:octicons-tag-24: 2.7.6](https://github.com/PyFPDF/fpdf2/blob/master/CHANGELOG.md)_

Can be set globally or per cell.
Works the same way as padding, but with the `v_align` parameter.

Can be set globally, per row or per cell, by passing a string or a [VAlign](https://py-pdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.VAlign) enum value as `v_align`:
```python

...
with pdf.table(v_align=VAlign.M) as table:
...
row.cell(f"custom v-align", v_align=VAlign.T) # <-- align to top
row.cell(f"custom v-align", v_align="TOP")
```

## Setting row height

First, `line_height` can be provided to set the height of every individual line of text:
```python
...
with pdf.table(line_height=2.5 * pdf.font_size) as table:
...
```

_New in [:octicons-tag-24: 2.8.3](https://github.com/py-pdf/fpdf2/blob/master/CHANGELOG.md)_

Second, a global `min_row_height` can be set,
or configured per row as `min_height`:
```python
...
with pdf.table(min_row_height=30) as table:
row = table.row()
row.cell("A")
row.cell("B")
row = table.row(min_height=50)
row.cell("C")
row.cell("D")
```

## Disable table headings

By default, `fpdf2` considers that the first row of tables contains its headings.
Expand Down
Loading
Loading