Images
galeries Mise à jour mai 2024
Fil des révisions
  • mai 2024
    Ajout de l'img-class pour indiquer au navigateur comment découper une image pour en faire un carré.
  • décembre 2023
    Mise à jour après une large simplification du CSS et en conséquence la récriture complète de cette page avec un gros effort pour pouvoir créer des illustrations en HTML/CSS et non pas sous la forme d'images.
  • mars 2022
    Création de la page

J'ai commencé ce site en pensant à des pages écrites, et j'ai aussi retrouvé des milliers de photos qui avaient un grand intérêt.

Bien évidemment leur publication m'a amené à résoudre tout une série de questions que cette page évoque.

Je précise que le résultat est une création personnelle dont je suis assez fier.

Galerie d'images

Des galeries d’images, il y en a sur tous les sites, c'est bien évidemment le résultat à obtenir.

Les premiers essais furent compliqués, les originaux étaient de formats différents, Paysages ou Portraits, , 6x9 pour les plaques de verre, 24x36 pour les pellicules, 16/9 pour les photos numériques pour ne citer que les plus fréquents ; disons que le résultat était pour le moins décevant :


ImageSize:		taille en pixels de la photo d'origine (dépend de la précision de la numérisation)
FileSize:	taille du fichier photo
col-md:		largeur de la photo, en colonnes
aspect-ratio:	rapport largeur / hauteur de la photo d'origine exprimée en ratio arrondi ou en nombre décimal
th:		thumb, taille en pixel de la photo affichée (en fonction de la taille attendue sur l'écran)

Rue montante		ImageSize:677x1010; FileSize:199 Ko; col-md:1; aspect-ratio:2/3-0,658; th:106x161
Femmes			ImageSize:2023x3095; FileSize:958 Ko; col-md:1; aspect-ratio:2/3-0,642; th:106x165
Alsacienne		ImageSize:2687x4042; FileSize:1 Mo; col-md:1; aspect-ratio:2/3-0,654; th:106x162
Enfants			ImageSize:1029x674; FileSize:168 Ko; col-md:3; aspect-ratio:3/2-1,531; th:326x213
Place			ImageSize:4032x1960; FileSize:3 Mo; col-md:3; aspect-ratio:3/2-1,531; th:326x213
Rue descendante		col-md:1; aspect-ratio:1/2-0,488; th:106x217

B0f_231213-2159_003
B0f_231213-2159_004
B0f_231213-2159_005
B0f_231213-2159_006
B0f_231213-2159_007
B0f_231213-2159_008

Commence alors un long chemin dont voici le résultat.

Largeur utile

Se pose alors la question de l'organisation du site en équilibrant la largeur du texte et des galeries de photos en tenant compte de toutes les largeurs d'écrans possibles. Pour un site composé principalement de texte, il est clair que sur les grands écrans les textes trop larges sont à proscrire :

Largeur 100%

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim.

Nous avons donc choisi une largeur maximum pour le texte ce qui nous a conduit, pour un résultat équilibré à limiter aussi la largeur des galeries d'images  :

Images 1320 pixels, texte 1140 px

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim.

Organisation des images

Images à la même hauteur

Pour organiser nos images dans ce cadre, la solution la plus simple est de découper les images en fonction de leur largeur en décidant qu’il suffira ensuite de leur donner à toute la même hauteur :

Ceci est possible parce que l'image originale est beaucoup plus grande que celle dont nous avons besoin que l'image ait été scannée ou soit issue d'un appareil numérique.

Pour donner des ordres de grandeur, une image scannée peut avoir une taille en pixels de 2687x4042 soit un fichier de 1 mb alors qu'une image issue d'un appareil numérique occupe 4032x1960 soit un fichier de 3 Mb

Le passage de la photo Turquoise au cadre rouge est décrit en fin de cette page.

Portrait
2232x349 soit 2/3
Paysage standard
232x155 soit 3/2
Paysage élargi
348x155 soit 2 1/4
Découpage en colonnes

Pour attribuer une largeur à nos images, nous allons commencer par découper le cadre en 12 colonnes :

Compter en base 12

Ce système a quelques avantages par rapport au système décimal dominant fonctionnant en base dix, dans la mesure où il permet de diviser par 2, 3, 4, et 6 et d’obtenir un résultat avec un nombre fini de chiffres après la virgule (au lieu de seulement 2 et 5 en décimal).

Le nombre douze est le plus petit nombre avec quatre facteurs non triviaux (2, 3, 4, 6), ce qui fait qu’il est plus agréable et facile à utiliser pour des calculs comme les multiplications ou les divisions.

1320 / 12 colonnes
1
110 x 110
2
3
4
5
6
7
8
9
10
11
12
Largeur de la galerie

A noter que cette façon de faire nous lie à la largeur de la galerie, ici nous l’avons réduit, et conserver nos 12 colonnes oblige à avoir des lignes plus fines :

1140 / 12 colonnes- repeat(auto-fill, minmax(8%, 1fr))
1
95 x 95
2
3
4
5
6
7
8
9
10
11
12

L'autre solution si l'on désire conserver la hauteur des lignes c'est de mettre moins de colonnes et des lignes plus épaisses :

1140 / 10 colonnes - repeat(auto-fill, minmax(10%, 1fr))
1
114 x 114
2
3
4
5
6
7
8
9
10

Géométrie des photos

Restons dans nos 12 colonnes et regardons comment y faire entrer nos différents formats de photos.

La solution consiste à abandonner le format carré et à utiliser des cadres au format 3/2 au motif que c'est celui des photos en 24/36 qui sont de très loin le format le plus utilisé dans nos archives :

1320 / 12 = 110
1
110x73
3/2
2
3
4
5
6
7
8
9
10
11
12

Mais s’arrêter à cela nécessiterait de découper pour les afficher toutes les photos en format Portrait ainsi que toutes celles prises avec des appareils récents dans d’autres formats. La solution consiste alors de permettre d'attribuer plusieurs colonnes à une image :

1 col.
2 colonnes
3 colonnes
6 colonnes

Mais c’est loin d’être suffisant, les images plus large sont trop plates. L’essai suivant consiste à donner le ratio 3/2 au cadre qui est sur 2 colonnes et de faire varier les ratios des autres cadres pour les ajuster sur la nouvelle hauteur :

1 col.
110x146,65
2 colonnes
220x146,65
3/2

C’est nettement mieux mais les photos sont encore un peu petites. Essayons le ratio 3/2 sur 3 colonnes  :

1 col.
110x220
3 colonnes
330x220
3/2

C'est cette dernière organisation que nous avons retenue et pour nous conforter dans notre décision, regardons le résultat avec une photo à qui nous allouons une colonne, puis deux, trois ou quatre :

col-md: nombre de colonnes allouées à la photo
fit:    appliquer pour n colonnes le ratio qui suit
th:     taille en pixel de l’image résultante

col-md:1; fit:1 3/2; th:114x76;
col-md/2; fit:2 3/2; th:235x152;
col-md:3; fit:3 3/2; th:357x228;
col-md:4; fit:4 3/2; th:477x304;

B0f_231213-2159_015
B0f_231213-2159_016
B0f_231213-2159_017
B0f_231213-2159_018

Dans toute la documentation, cette taille cible est indiquée sur chaque photo par l'attribut fit:3 3/2 qui indique que l'on est dans un système où la photo de référence en ratio 3/2 occupe 3 colonnes.

Notons qu’avec cette notation nous pouvons aussi générer des images pour d’autres formats, les photos précédentes ont été créées avec fit:1 3/2 , fit :2 3/2, fit:4 3/2.

Recadrer l'image

Nous disposons désormais d'une valeur cible sur l'axe des y issue du fit:3 3/2, il est maintenant possible d'agir sur l'axe des x et de faire varier le nombre de colonnes, ce qui va nécessiter à chaque fois de couper la partie de l'image qui dépasse.

L'attribut qui indique comment découper (crop en anglais) la photo :

crop:+0%[10+0; gravity:east;	[10 indique que 10% de l'image est à découper sur la droite de l'image
crop:+0+20%[32;			[32 pour 32% qui dépasse mais ici sur la hauteur
				sans indication de gravité, le découpage se fera sur chaque moitié à partir du centre
				+20% indique qu'il faut déplacer la référence de 20% vers le bas avant le découpage
crop:+0+25%[75;
crop:+0+22%[117;

gravity:	absent si référence au centre ou north, south, east, west pour découper sur le côté opposé.
		L'intérêt de la gravity sur un côté est qu'elle ne varie pas avec le nombre de colonnes.

Ici, nous avons essayé 1, 2, 3 ou 4 colonnes :

B0f_231213-2159_021 B0f_231213-2159_022 B0f_231213-2159_023 B0f_231213-2159_024

Galerie sur une ligne

Nous pouvons maintenant réaliser une galerie tout à fait acceptable que nous affichons pour l'exemple dans la même largeur que le texte :

Comme déjà expliqué, après avoir réduit la largeur de l'affichage, il est nécessaire de réduire le nombre de colonnes à 10 pour conserver la même hauteur d'image :


.w1140 {
    width: 1140px;
    grid-template-columns: repeat(auto-fill, minmax(10%, 1fr));
}

Les images ont été dimensionnées pour 10 colonnes, le code VBA récupérant le tag w1140 et effectue alors ses calculs avec 10 colonnes.

Je n’ai pas fait tout le code CSS nécessaire à cet affichage qui aurait été très peu utilisé.

B0f_231213-2159_027 B0f_231213-2159_028 B0f_231213-2159_029 B0f_231213-2159_030 B0f_231213-2159_031 B0f_231213-2159_032

Ce résultat, tout à fait réussi, est cependant un appel à faire mieux encore parce que la technologie le permet.

Galerie sur plusieurs lignes

CSS offre avec grid ou flex deux technologies pour afficher des grilles. De notre point de vue, le flex permet d’afficher des séries de lignes uniformes comme celle qui est au-dessus et le grid des tableaux complexes d’images disparates, comme ici :

Nous avons donc ajouté un attribut pour indiquer le nombre de lignes

row-span: absent pour 1, sinon 2 ou 3 pour le nombre de lignes à utiliser pour la photo.

B0f_231213-2159_034 B0f_231213-2159_035 B0f_231213-2159_036 B0f_231213-2159_037 B0f_231213-2159_038 B0f_231213-2159_039

Et nous nous arrêterons là parce que pour le coup c'est assez réussi !

Largeur d’écran

L'étape suivante consiste maintenant à utiliser les possibilités des grid pour s'adapter aux écrans étroits .

Mobile first

La théorie du CSS engage tous les designers à penser au mobile avec son écran étroit en premier pour concevoir un site et ensuite voir comment le dériver pour l’adapter aux écrans larges.

Les forums fournissent en réponse tous les arguments pour au contraire commencer par les écrans larges.

J'en fournis un qui me convient : tant qu'à concevoir une galerie de photos, autant le faire sur un grand écran.

La solution consiste à réduire progressivement le nombre de colonnes de la grille puis à passer aux photos carrées pour leur conserver une taille suffisante lorsque l'écran devient trop petit pour afficher à la fois Portrait et Paysages :

12 colonnes
1
2
3
4
5
6
9 colonnes
1
2
3
4
5
6
La réorganisation effectuée par le grid fonctionne mieux avec des images plus uniformes.
3 colonnes
1
2
3
4
5
6
Les photos Portraits 1 et 4 seront déformées pour s’afficher dans un cadre plus large.
2 colonnes
1
2
3
4
5
6
Découpe par le navigateur pour obtenir un carré

C'est le navigateur qui détermine s'il doit afficher l'image dans sa géométrie d'origine où s'il doit en faire un carré, mais le résultat sera meilleur s'il est possible de lui indiquer la partie à conserver.

img-class:op-top;
img-class:op-bottom;
img-class:op-25;

Dans le XML, un tag permet de rajouter une classe à l’image pour positionner l’object-position pour le recadrage.


<xdoc file="…" img="…" key="… img-class:op-top; …"/>

Dans la page HTML, la nouvelle classe sera transformée en code CSS de la façon suivante :

.op-top { object-position: top; }/* préciser comment la photo doit être coupée pour les petits affichages */ .op-bottom { object-position: bottom; } .op-25 { object-position: 0 25%; } .op-75 { object-position: 0 75%; }

Adapter la taille de l’image à celle de l’écran

Nous utilisons deux images, l’une comme vignette, l’autre pour l’affichage en grand dans le carousel. Les images carrées sont seulement produites par le navigateur qui redécoupe la vignette. Pour les images étroites qu’il agrandit, la définition devient insuffisante et pour toute, le filigrane n’est plus au bon endroit.

J'aurais donc pu fournir une troisième image mais cela aurait consisté à rajouter de la complexité pour un résultat assez minime.

Toute la largeur de l'écran

De temps en temps, il est souhaitable de pouvoir afficher une photo sur la moitié voire toute la largeur de l’écran. Le ratio 3/2 n'est plus utilisable parce qu'il donne des photos qui occupent trop de place en hauteur.

L'affichage en 12 colonnes avec fit:6 3/2 essayé ici convient bien.

B0f_231213-2159_044

Texte entre deux images

L’emploi n’est pas fréquent, mais cela peut arriver comme dans la page de Louis et Geneviève.
B0f_231213-2159_049

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus.

B0f_231213-2159_050

La mise en œuvre passe par un tag <div> dans le texte associé à la photo de droite :


<fixed>
 <div class="ms-3 me-3 sp6t">
  <blockquote class="blockquote hubert">
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus.</p>    
  </blockquote>
 </div>
</fixed>

Texte qui enveloppe l’image

B0f_231213-2159_052

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi.

Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue.

Praesent egestas leo in pede. Praesent blandit odio eu enim.

Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.

Rien à faire dans ce cas, tout est déjà dans le CSS :


.blog img { max-width:100%; }		/* pour réduire aussi les images dans les blogs et leur éviter de prendre la place du texte s'il y a lieu */

Décrire une galerie

L’idée ici est de disposer d’une description de la galerie qui soit indépendante du code HTMLXSS qui sera utilisé par la suite pour l’afficher.

Chaque image sera décrite en XML par un tag xdoc ; l'attribut img décrit l'image source et les deux images du site (web et th), l’attribut key décrit ce que nous voulons obtenir :

ExifTool by Phil Harvey

L’analyse des tags à l’intérieur des images est complexe techniquement mais aussi en raison de la très grande diversité des formats créés par tous les constructeurs d’appareils de photos, de smartphones et de scanners.

ExifTool le fait parfaitement et c'est grace à lui qu'il est possible d’afficher la date de création, l’orientation de la photo, le lieu de la prise de vue ou les coordonnées GPS.

La durée d’accès à l’image avec ExifTool se mesure en secondes, c'est la raison pour laquelle les valeurs sont recopiées dans le xdoc pour en disposer plus rapidement. Le CMS propose une fonction clean qui va relire ces valeurs dans l’image après qu'elle ait été modifiée. Mais ceci arrive très rarement.


<xdoc file="Sommaire\ImageGallery\B0f_220325-0938_035"
      img="ImageSize:4032x1960; FileSize:4 Mb; CreateDate:2020:08:09; Orientation:Rotate 90 CW; web:389x800; th:353x692"
      key="row-class:galerie; col-md:3; row-span:3; fit:3 3/2; aspect-ratio:13/25-0,51; th:353x692; crop:+0+0%[2;">
<com>col-md:3; row-span:3; fit:3 3/2; th:353x692; crop:+0+0%[5;</com>
</xdoc>

Les tags xdoc pour les autres fichiers de la galerie sont ici .


<xdoc file="Sommaire\ImageGallery\B0f_220325-0938_035" 
   img="ImageSize:4032x1960; FileSize:4 Mb; CreateDate:2020:08:09; Orientation:Rotate 90 CW; web:389x800; th:353x692" 
   key="row-class:galerie; col-md:3; row-span:3; fit:3 3/2; aspect-ratio:13/25-0,51; th:353x692; crop:+0+0%[2;">
<com>col-md:3; row-span:3; fit:3 3/2; th:353x692; crop:+0+0%[5;</com>
</xdoc>

Html\Sommaire\ImageGallery\B0f_220325-0938_036.jpg=Nam:Sommaire\ImageGallery\B0f_220325-0938_036|
<xdoc file="Sommaire\ImageGallery\B0f_220325-0938_036" 
   img="ImageSize:1029x674; FileSize:168 Kb; Location:Prats-de-Mollo-la-Preste; GPSposition:42.403823 2.478684; web:4032x1960; th:715x460" 
   key="row-class:galerie; col-md:6; row-span:2; fit:3 3/2; aspect-ratio:14/9-1,554; th:715x460; crop:+0+0%[1;">
<com>col-md:6; row-span:2; fit:3 3/2; th:715x460; crop:-0%[24+0;</com>
</xdoc>

<xdoc file="Sommaire\ImageGallery\B0f_220325-0938_037"
   img="ImageSize:2687x4042; FileSize:1 Mb; CreateDate:1969:09:01; web:532x800; th:353x460"
   key="row-class:galerie; col-md:3; row-span:2; fit:3 3/2; aspect-ratio:10/13-0,767; th:353x460; crop:+0+0%[8; gravity:south;">
<com>col-md:3; row-span:2; fit:3 3/2; th:353x460; crop:+0+0%[8; gravity:south;</com>
</xdoc>

<xdoc file="Sommaire\ImageGallery\B0f_220325-0938_038"
  img="ImageSize:2023x3095; FileSize:958 Kb; Location:Prats-de-Mollo-la-Preste; GPSposition:42.403823 2.478684; web:523x800; th:357x228"
  key="row-class:galerie; col-md:3; fit:3 3/2; aspect-ratio:11/7-1,566; th:357x228; crop:+0+0%[70; gravity:south;">
<com>col-md:3; fit:3 3/2; th:357x228; crop:+0+0%[70; gravity:south;</com>
</xdoc>

<xdoc file="Sommaire\ImageGallery\B0f_220325-0938_039"
  img="ImageSize:1029x674; FileSize:168 Kb; Location:Prats-de-Mollo-la-Preste; GPSposition:42.403823 2.478684; web:1029x674; th:477x228"
  key="row-class:galerie; col-md:4; fit:3 3/2; aspect-ratio:21/10-2,092; th:477x228; crop:+0+0%[18; gravity:north;">
<com>ImageSize:1029x674; col-md:4; fit:3 3/2; aspect-ratio:21/10-2,092; th:477x228; crop:+0+0%[27; gravity:north;</com>
</xdoc>

<xdoc file="Sommaire\ImageGallery\B0f_220325-0938_040"
  img="ImageSize:677x1010; FileSize:199 Kb; CreateDate:2020:04:28; Location:Prats-de-Mollo-la-Preste; GPSposition:42.403823 2.478684; web:536x800; th:235x228"
  key="row-class:galerie; col-md:2; fit:3 3/2; aspect-ratio:26/25-1,031; th:235x228; crop:+0+0%[27; gravity:south;">
<com>col-md:2; fit:3 3/2; th:235x228; crop:+0+0%[27; gravity:south;</com>
</xdoc>

B0f_231213-2159_054

Notre CMS offre les fonctions nécessaires à la manipulation des tags xdoc.

Dans l'image affichée ci-dessus, la taille de la photo originale est en pixels de 4032x1960, la taille de la vignette pour la galerie est de 353x692.

Le passage de l’une à l’autre image réside de la mise en œuvre des attributs col-md:3; row-span:3; fit:3 3/2; crop:+0+0%[2;.

C'est là qu'intervient notre CMS qui nous propose dans le menu toutes les valeurs possibles et se charge des calculs, de la mise à jour du tag xdoc, de la création de la vignette et de son affichage à l’écran pour contrôle.

Afficher une galerie

Pour les images :


.image {
   border: 1px solid #ccc;
   box-shadow: 2px 2px 6px 0px rgba(0,0,0,0.3);
   max-width: 100%;                /* utiliser tout l’espace disponible */
   object-fit: cover;              /* the image keeps its aspect ratio and fills the given dimension, the image will be clipped to fit */
   }
   .blog img { max-width:100%; }/* pour réduire aussi les images dans les blogs et leur éviter de prendre la place du texte s’il y a lieu */
      
      .image-left {
         float: left;     /* pour faire flotter à gauche les images dans les blogs */
      }
      .image-right {
         float: right;       /* idem pour la droite */
      }
      @media (min-width: 576px) {         /* destiné aux images dans les blogs */
         .image-left {
            margin-right: 0.5em;       /* par défaut, l’image est à gauche de la page */
            margin-left: unset;
         }
         .image-right {
            margin-right: unset;
            margin-left: 0.5em;        /* s’il y a du texte dans le <float> */
         }

Et pour les galeries :



.galerie {
   grid-template-columns: repeat(auto-fill, minmax(49%, 1fr));/* 2 colonnes pas ligne, 50% = 49 + 1 pour grid-gap */
   display: grid;
   grid-auto-flow: dense;/* Place items to fill any holes in the grid */
   align-items: stretch;/* Items are stretched to fit the container  */
   grid-gap: 2px;
   break-inside: avoid;
   }
   .sp1  {
      grid-column: auto / span 1;
      aspect-ratio: 1;/* pour un affichage sur 2 colonnes qui va agrandir et découper l’image */
      width: 100%;
   }
   .sp2, .sp3, .sp4, .sp5 {
      grid-column: auto / span 1;/* tout le monde sur une seule colonne */
      aspect-ratio: 1;/* affichage en carré */
   }
   .sp6, .sp7, .sp8, .sp9, .sp10, .sp11, .sp12  {
      grid-column: auto / span 2;
   }
   .sp6t  {grid-column: auto / span 2;}/* sp6t texte dans un grid, pour Louis */
      
      @media (min-width: 360px) {
         .galerie {
            grid-template-columns: repeat(auto-fill, minmax(33%, 1fr));/* 3 colonnes par ligne */
         }
         .blog img {
            max-width:50%;
         }
         .sp6, .sp7, .sp8, .sp9, .sp10, .sp11, .sp12  {
            grid-column: auto / span 3;
         }
      .sp6t  {grid-column: auto / span 3;}
      }
      
      @media (min-width: 768px) {
         .galerie {
            grid-template-columns: repeat(auto-fill, minmax(10%, 1fr));/* 9 colonnes par ligne 10% = 9 + 1 pour grid-gap */
         }
      .sp1 {                                         width: unset;}
      .sp2 {             grid-column: auto / span 2; aspect-ratio: unset;}
      .sp3, .sp4, .sp5 { grid-column: auto / span 3; aspect-ratio: unset;}
      .sp6, .sp7, .sp8 { grid-column: auto / span 3;}
         
      .sp12  { grid-column: auto / span 12; aspect-ratio: unset;}
         
      .sp6t  {grid-column: auto / span 9;}/* texte dans un grid */
         
      .row2 { grid-row: span 2; }           /* image sur 2 lignes */
      .row3 { grid-row: span 3; }
      }
      
      @media (min-width: 1140px) {
         .galerie {
            grid-template-columns: repeat(auto-fill, minmax(8%, 1fr));    /* 12 colonnes par ligne 8% = 8 + 0.333 pour le grid-gap */
         }
      .sp4   { grid-column: auto / span  4;}
      .sp5   { grid-column: auto / span  5;}
      .sp6   { grid-column: auto / span  6;}
      .sp7   { grid-column: auto / span  7;}
      .sp8   { grid-column: auto / span  8;}
         /*
      .sp9   { grid-column: auto / span  9;}
      .sp10  { grid-column: auto / span 10;}
      .sp11  { grid-column: auto / span 11;}
         */
      .sp6t  {grid-column: auto / span 6;}
}

Création de la vignette (thumbnail)

La librairie ImageMagick fournit les fonctions nécessaires pour manipuler les images.

Le code ci-dessous permet de :

Ici, ça à l’air compliqué et ça l’a été ! Là encore, c'est une implémentation personnelle, beaucoup de forums traitaient de la question, mais aucun n'a pas apporté toute la solution ainsi présentée.


If Not keyprop.exist("crop") And Not keyprop.exist("thumb") Then
   msg = img.Convert(cdoc.JpgSourceFile, "-auto-orient", "-thumbnail", tx & "x" & ty, cdoc.JpgThumbFile)
Else
   If keyprop.exist("crop") Then
      cropParam = keyprop("crop").crop
      crx = cropParam(0)
      cry = cropParam(1)
   Else
      crx = 0
      cry = 0
   End If
   reducX = s(0) / tx
   reducY = s(1) / ty

   If reducX > reducY Then
      msize = s(0) / IIf(reducY = 0, 1, reducY)
      resize = Int(msize) & "x" & ty & "^"    ' ^ to resize based on the smallest fitting dimension : et on coupe sur l’autre
      cropExtent = Format(msize * crx / 100, "+0;-0;+0") & "+0"
   Else
      msize = s(1) / IIf(reducX = 0, 1, reducX)
      resize = tx & "x" & Int(msize) & "^"
      cropExtent = "+0" & Format(msize * cry / 100, "+0;-0;+0") ' le ratio est appliqué sur la valeur cible, pas celle d’origine
   End If
   
If reducX > reducY Then noShave = crx <> 0 And cry = 0 Else noShave = crx = 0 And cry <> 0
   
   If noShave Then
      If crx = 0 And cry = 0 Then
         msg = img.Convert(cdoc.JpgSourceFile, "-auto-orient", "+repage", "-gravity", gravity, _
                                               "-thumbnail", resize, "-extent", tx & "x" & ty, cdoc.JpgThumbFile)
      Else
         msg = img.Convert(cdoc.JpgSourceFile, "-auto-orient", "+repage", "-gravity", gravity, _
                                               "-thumbnail", resize, "-extent", tx & "x" & ty & cropExtent, cdoc.JpgThumbFile)
      End If
   Else
      If gravity = "center" Then
          If reducX > reducY Then shavecmd = "0%x" & Abs(cry) Else shavecmd = Abs(crx) & "%x0"
              ' shave enlève la même zone de chaque coté
          msg = img.Convert(cdoc.JpgSourceFile, "-auto-orient", "+repage", "-gravity", gravity, _
                                                 "-define", "jpeg:size=" & tx * 2 & "x" & ty * 2, "-shave", shavecmd, _
                                                 "-thumbnail", resize, _
                                                 "-extent", tx & "x" & ty & cropExtent, cdoc.JpgThumbFile)
      Else
         If reducX > reducY Then
            crop1arge = "+0" & Format(s(1) * cry / 100, "+0;-0;+0")
         Else
            crop1arge = Format(s(0) * crx / 100, "+0;-0;+0") & "+0"
         End If
         ' le crop va couper à partir de la gravité qui aura été donnée
         msg = img.Convert(cdoc.JpgSourceFile, "-auto-orient", "+repage", "-gravity", gravity, _
                                               "-define", "jpeg:size=" & tx * 2 & "x" & ty * 2, _
                                               "-crop", s(0) & "x" & s(1) & crop1arge, _
                                               "-thumbnail", resize, "-extent", tx & "x" & ty & cropExtent, cdoc.JpgThumbFile)
      End If
   End If
End If

Filigrane (Watermark)

L’ajout du filigrane est assez bien documenté dans ImageMagik, pour une fois le couper / coller a pu fonctionner. Le code supplémentaire détermine l’endroit où le placer ainsi que sa taille en fonction de la taille de l’image.

Pour optimiser un peu, le filigrane intermédiaire est conservé dans un fichier pour réutilisation.


Private Function addWatermark(img As Object, dest As String, thumb As String, tx As Long, ty As Long, wm As String) As String
Dim Wmask As String, Wstamp As String, Wtm As String, tlen As Integer, twidth As Integer, tsize As Integer
Dim paramxml As New cParamXML

If tx > ty Then
   tsize = ty
Else
   tsize = tx
End If

If tsize <= 80 Then
   wm = "to narrow for Watermark"
   addWatermark = ""
   Exit Function
End If

tlen = ((Int(tsize / 100)) * 100)                 ' évitons d’avoir trop de fichiers Watermarks différents
Select Case tlen
         Case Is <= 200: twidth = 25
         Case 300: twidth = 35
         Case 400: twidth = 45
         Case 500: twidth = 50
         Case 600: twidth = 60
         Case Else: twidth = 70
End Select

Wtm = ActiveDocumentPath & "\ImageMagick\W_" & wm & " " & tlen & ".png"
If Dir(Wtm) = "" Then ' transparent
   Wstamp = ActiveDocumentPath & "\ImageMagick\Wstamp.png"
   Wmask = ActiveDocumentPath & "\ImageMagick\Wmask.jpg"

' agrandissement puis réduction de l’image pour réduire un peu la netteté
   msg = img.Convert("-size", Int(tlen * 1.2) & "x" & Int(twidth * 1.2), "-pointsize", Int(twidth * 0.7), _
                     "xc:transparent", "-font", "Arial Black", "-gravity", "center", _
                     "-fill", "black", "-annotate", "-1-1", wm, _
                     "-fill", "white", "-annotate", "+1+1", wm, _
                     "-fill", "transparent", "-annotate", "+0+0", wm, Wstamp)

   msg = img.Convert("-size", Int(tlen * 1.2) & "x" & Int(twidth * 1.2), "-pointsize", Int(twidth * 0.7), _
                     "xc:black", "-font", "Arial Black", "-gravity", "center", _
                     "-fill", "white", "-annotate", "-1-1", wm, _
                     "-fill", "white", "-annotate", "+1+1", wm, _
                     "-fill", "black", "-annotate", "+0+0", wm, Wmask)

   msg = img.composite("-compose", "CopyOpacity", Wmask, Wstamp, "-resize", tlen & "x", Wtm)
   Kill Wstamp
   Kill Wmask
End If

If tx > ty Then
    addWatermark = img.composite("-gravity", "west", Wtm, "-rotate", "-90", dest, "-rotate", "90", dest)
Else
   addWatermark = img.composite("-gravity", "south", Wtm, dest, dest)
End If
End Function