|
| 1 | +# fpdf2 internals |
| 2 | + |
| 3 | +## FPDF.pages |
| 4 | +`FPDF` is designed to add content progressively to the document generated, page by page. |
| 5 | + |
| 6 | +Each page is an entry in the `.pages` attribute of `FPDF` instances. |
| 7 | +Indices start at 1 (the first page) and values are [`PDFPage`](https://py-pdf.github.io/fpdf2/fpdf/output.html#fpdf.output.PDFPage) instances. |
| 8 | + |
| 9 | +`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 |
| 10 | +(`bytearray` makes things [a lot faster](https://github.com/reingart/pyfpdf/pull/164)). |
| 11 | + |
| 12 | +Going back to a previously generated page to add content is possible, |
| 13 | +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. |
| 14 | + |
| 15 | + |
| 16 | +## syntax.py & objects serialization |
| 17 | +The [syntax.py](https://github.com/py-pdf/fpdf2/blob/master/fpdf/syntax.py) package contains classes representing core elements of the PDF syntax. |
| 18 | + |
| 19 | +Classes inherit from the [PDFObject](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.PDFObject) class, that has the following properties: |
| 20 | + |
| 21 | +* every PDF object has an `.id`, that is assigned during the document serialization by the [OutputProducer](#outputproducer) |
| 22 | +* the `.serialize()` method renders the PDF object as an `obj<</>>endobj` text block. It can be overridden by child classes. |
| 23 | +* the `.content_stream()` method must return non empty bytes if the PDF Object has a _content stream_ |
| 24 | + |
| 25 | +Other notable core classes are: |
| 26 | + |
| 27 | +* [Name](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.Name) |
| 28 | +* [Raw](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.Raw) |
| 29 | +* [PDFString](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.PDFString) |
| 30 | +* [PDFArray](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.PDFArray) |
| 31 | +* [PDFDate](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.syntax.PDFDate) |
| 32 | + |
| 33 | + |
| 34 | +## GraphicsStateMixin |
| 35 | +This _mixin_ class, inherited by the `FPDF` class, |
| 36 | +allows to manage a stack of graphics state variables: |
| 37 | + |
| 38 | +* docstring: [fpdf.graphics_state.GraphicsStateMixin](https://py-pdf.github.io/fpdf2/fpdf/graphics_state.html#fpdf.graphics_state.GraphicsStateMixin) |
| 39 | +* source file: [graphics_state.py](https://github.com/py-pdf/fpdf2/blob/master/fpdf/graphics_state.py) |
| 40 | + |
| 41 | +The main methods of this API are: |
| 42 | + |
| 43 | +* [_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 |
| 44 | +* [_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 |
| 45 | +* [_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 |
| 46 | +* [_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) |
| 47 | + |
| 48 | +Thanks to this _mixin_, we can use the following semantics: |
| 49 | +```python |
| 50 | +{% include "../tutorial/graphics_state.py" %} |
| 51 | +``` |
| 52 | + |
| 53 | +The graphics states used in the code above |
| 54 | +can be depicted by this diagram: |
| 55 | + |
| 56 | +``` mermaid |
| 57 | +stateDiagram-v2 |
| 58 | + direction LR |
| 59 | + state gs0 { |
| 60 | + initial1 : Base state |
| 61 | + } |
| 62 | + state gs1 { |
| 63 | + initial2 : Base state |
| 64 | + font_size_pt2 : font_size_pt=16 |
| 65 | + underline2 : underline=True |
| 66 | + font_size_pt2 --> initial2 |
| 67 | + underline2 --> font_size_pt2 |
| 68 | + } |
| 69 | + gs0 --> gs1: Step 1 |
| 70 | + state "gs0" as stack2 { |
| 71 | + initial3 : Base state |
| 72 | + } |
| 73 | + gs1 --> stack2: Step 2 |
| 74 | +``` |
| 75 | + |
| 76 | + |
| 77 | +## OutputProducer |
| 78 | +In `fpdf2`, the `FPDF` class is used to store the document **definition**, |
| 79 | +its state as it is progressively built. Most attributes and internal data is **mutable**. |
| 80 | + |
| 81 | +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. |
| 82 | + |
| 83 | +It performs the serialization of the PDF document, |
| 84 | +including the generation of the [cross-reference table & file trailer](https://py-pdf.github.io/fpdf2/fpdf/output.html#fpdf.output.PDFXrefAndTrailer). |
| 85 | +This class uses the `FPDF` instance as **immutable input**: |
| 86 | +it does not perform any modification on it. |
| 87 | + |
| 88 | +<!-- Other topics to mention: |
| 89 | +
|
| 90 | +## Vector Graphics |
| 91 | +drawing.py & svg.py packages |
| 92 | +
|
| 93 | +## Text regions & flow ? |
| 94 | +
|
| 95 | +## Text shaping ? |
| 96 | +
|
| 97 | +--> |
0 commit comments