1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
|
# Images #
When rendering an image, its size on the page can be specified in several ways:
* explicit width and height (expressed in user units).
The image is scaled to those dimensions, unless `keep_aspect_ratio=True` is specified.
* one explicit dimension, the other being calculated automatically in order to keep the original proportions
* no explicit dimension, in which case the image is put at 72 dpi
Note that if an image is displayed several times, only one copy is embedded in the file.
## Simple example ##
```python
from fpdf import FPDF
pdf = FPDF()
pdf.add_page()
pdf.image("docs/fpdf2-logo.png", x=20, y=60)
pdf.output("pdf-with-image.pdf")
```
By default an image is rendered with a resolution of 72 dpi,
but you can control its dimension on the page using the `w=` & `h=` parameters of the [`image()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.image) method.
## Alpha / transparency ##
`fpdf2` allows to embed images with alpha pixels.
Technically, it is implemented by extracting an `/SMask` from images with transparency,
and inserting it along with the image data in the PDF document. Related code is in the [image_parsing]( https://github.com/py-pdf/fpdf2/blob/master/fpdf/image_parsing.py) module.
## Assembling images ##
The following code snippets provide examples of some basic layouts for assembling images into PDF files.
### Side by side images, full height, landscape page ###
```python
from fpdf import FPDF
pdf = FPDF(orientation="landscape")
pdf.set_margin(0)
pdf.add_page()
pdf.image("imgA.png", h=pdf.eph, w=pdf.epw/2) # full page height, half page width
pdf.set_y(0)
pdf.image("imgB.jpg", h=pdf.eph, w=pdf.epw/2, x=pdf.epw/2) # full page height, half page width, right half of the page
pdf.output("side-by-side.pdf")
```
### Fitting an image inside a rectangle ###
When you want to scale an image to fill a rectangle, while keeping its aspect ratio,
and ensuring it does **not** overflow the rectangle width nor height in the process,
you can set `w` / `h` and also provide `keep_aspect_ratio=True` to the [`image()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.image) method.
The following unit tests illustrate that:
* [test_image_fit.py](https://github.com/py-pdf/fpdf2/blob/master/test/image/test_image_fit.py)
* resulting document: [image_fit_in_rect.pdf](https://github.com/py-pdf/fpdf2/blob/master/test/image/image_fit_in_rect.pdf)
### Blending images ###
You can control the color blending mode of overlapping images.
Valid values for `blend_mode` are `Normal`, `Multiply`, `Screen`, `Overlay`, `Darken`, `Lighten`, `ColorDodge`,
`ColorBurn`, `HardLight`, `SoftLight`, `Difference`, `Exclusion`, `Hue`, `Saturation`, `Color` and `Luminosity`.
```python
from fpdf import FPDF
pdf = FPDF()
pdf.add_page()
pdf.image("imgA.png", ...)
with pdf.local_context(blend_mode="ColorBurn"):
pdf.image("imgB.jpg", ...)
pdf.output("blended-images.pdf")
```
Demo of all color blend modes: [blending_images.pdf](https://github.com/py-pdf/fpdf2/blob/master/test/drawing/generated_pdf/blending_images.pdf)
## Image clipping ##

You can select only a portion of the image to render using clipping methods:
* [`rect_clip()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.rect_clip):
- [example code](https://github.com/py-pdf/fpdf2/blob/master/test/image/test_image_clipping.py#L10)
- [resulting PDF](https://github.com/py-pdf/fpdf2/blob/master/test/image/rect_clip.pdf)
* [`round_clip()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.round_clip):
- [example code](https://github.com/py-pdf/fpdf2/blob/master/test/image/test_image_clipping.py#L33)
- [resulting PDF](https://github.com/py-pdf/fpdf2/blob/master/test/image/round_clip.pdf)
* [`elliptic_clip()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.elliptic_clip):
- [example code](https://github.com/py-pdf/fpdf2/blob/master/test/image/test_image_clipping.py#L56)
- [resulting PDF](https://github.com/py-pdf/fpdf2/blob/master/test/image/elliptic_clip.pdf)
## Alternative description ##
A textual description of the image can be provided, for accessibility purposes:
```python
pdf.image("docs/fpdf2-logo.png", x=20, y=60, alt_text="Snake logo of the fpdf2 library")
```
## Usage with Pillow ##
You can perform image manipulations using the [Pillow](https://pillow.readthedocs.io/en/stable/) library,
and easily embed the result:
```python
from fpdf import FPDF
from PIL import Image
pdf = FPDF()
pdf.add_page()
img = Image.open("docs/fpdf2-logo.png")
img = img.crop((10, 10, 490, 490)).resize((96, 96), resample=Image.NEAREST)
pdf.image(img, x=80, y=100)
pdf.output("pdf-with-image.pdf")
```
## SVG images ##
SVG images passed to the [`image()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.image) method
will be embedded as [PDF paths](SVG.md):
```python
from fpdf import FPDF
pdf = FPDF()
pdf.add_page()
pdf.image("SVG_logo.svg", w=100)
pdf.output("pdf-with-vector-image.pdf")
```
## Retrieve images from URLs ##
URLs to images can be directly passed to the [`image()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.image) method:
```python
pdf.image("https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png")
```
## Image compression ##
By default, `fpdf2` will avoid altering or recompressing your images: when possible, the original bytes from the JPG or TIFF file will be used directly. Bitonal images are by default compressed as TIFF Group4.
However, you can easily tell `fpdf2` to embed all images as JPEGs in order to reduce your PDF size,
using [`set_image_filter()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_image_filter):
```python
from fpdf import FPDF
pdf = FPDF()
pdf.set_image_filter("DCTDecode")
pdf.add_page()
pdf.image("docs/fpdf2-logo.png", x=20, y=60)
pdf.output("pdf-with-image.pdf")
```
The allowed `image_filter` values are listed in the [`set_image_filter()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_image_filter) method documentation.
Beware that "flattening" images into JPEGs this way will fill transparent areas of your images with color (usually black).
## Output Intents ##
_New in [:octicons-tag-24: 2.8.3](https://github.com/py-pdf/fpdf2/blob/master/CHANGELOG.md)_
> Output Intents [allow] the contents of referenced icc profiles to be embedded directly within the body of the PDF file. This makes the PDF file a self-contained unit that can be stored or transmitted as a single entity.
The dedicated method for adding output intent to a PDF is [`add_output_intent()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.add_output_intent).
You can optionally provide a [`PDFICCProfileObject`](https://py-pdf.github.io/fpdf2/fpdf/output.html#fpdf.output.PDFICCProfileObject) as `icc_profile`.
Example:
```python
from pathlib import Path
from fpdf import FPDF
from fpdf.enums import OutputIntentSubType
from fpdf.output import PDFICCProfileObject
HERE = Path(__file__).resolve().parent
pdf = FPDF()
with open(HERE / "sRGB2014.icc", "rb") as iccp_file:
icc_profile = PDFICCProfileObject(
contents=iccp_file.read(), n=3, alternate="DeviceRGB"
)
pdf.add_output_intent(
OutputIntentSubType.PDFA,
"sRGB",
'IEC 61966-2-1:1999',
"http://www.color.org",
icc_file,
"sRGB2014 (v2)",
)
```
The needed profiles and descriptions can be found at [International Color Consortium](https://color.org/).
## ICC Profiles
The ICC profile of the included images are read through the PIL function `Image.info.get("icc_profile)"` and are included in the PDF as objects.
An ICC profile can also be added by using the [`.add_output_intent()` method](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.add_output_intent),
as described in the previous section.
## Oversized images detection & downscaling ##
If the resulting PDF size is a concern,
you may want to check if some inserted images are _oversized_,
meaning their resolution is unnecessarily high given the size they are displayed.
There is how to enable this detection mechanism with `fpdf2`:
```python
pdf.oversized_images = "WARN"
```
After setting this property, a `WARNING` log will be displayed whenever an oversized image is inserted.
`fpdf2` is also able to automatically downscale such oversized images:
```python
pdf.oversized_images = "DOWNSCALE"
```
After this, oversized images will be automatically resized, generating `DEBUG` logs like this:
```
OVERSIZED: Generated new low-res image with name=lowres-test.png dims=(319, 451) id=2
```
For finer control, you can set `pdf.oversized_images_ratio` to set the threshold determining if an image is oversized.
If the concepts of "image compression" or "image resolution" are a bit obscure for you,
this article is a recommended reading:
[The 5 minute guide to image quality](https://medium.com/unsplash/the-5-minute-guide-to-image-quality-ad7c3503c845)
## Disabling transparency ##
By default images transparency is preserved:
alpha channels are extracted and converted to an embedded `SMask`.
This can be disabled by setting `.allow_images_transparency`,
_e.g._ to allow compliance with [PDF/A-1](https://en.wikipedia.org/wiki/PDF/A#Description):
```python
from fpdf import FPDF
pdf = FPDF()
pdf.allow_images_transparency = False
pdf.set_font("Helvetica", size=15)
pdf.cell(w=pdf.epw, h=30, text="Text behind. " * 6)
pdf.image("docs/fpdf2-logo.png", x=0)
pdf.output("pdf-including-image-without-transparency.pdf")
```
This will fill transparent areas of your images with color (usually black).
_cf._ also documentation on [controlling transparency](Transparency.md).
## Page background ##
_cf._ [Per-page format, orientation and background](PageFormatAndOrientation.md#per-page-format-orientation-and-background)
## Sharing the image cache among FPDF instances ##
Image parsing is often the most CPU & memory intensive step when inserting pictures in a PDF.
If you create several PDF files that use the same illustrations,
you can share the images cache among FPDF instances:
```python
image_cache = None
for ... # loop
pdf = FPDF()
if image_cache is None:
image_cache = pdf.image_cache
else:
pdf.image_cache = image_cache
... # build the PDF
pdf.output(...)
# Reset the "usages" count, to avoid ALL images to be inserted in subsequent PDFs:
image_cache.reset_usages()
```
This recipe is valid for `fpdf2` v2.5.7+.
For previous versions of `fpdf2`, a _deepcopy_ of `.images` must be made,
(_cf._ [issue #501](https://github.com/py-pdf/fpdf2/issues/501#issuecomment-1224310277)).
|