#!/usr/bin/perl
# =============================================================================
# Rapatrie les polices de la charte pour les servir depuis notre domaine.
#
# POURQUOI. Le gabarit chargeait Lora, Quicksand et Raleway depuis
# fonts.googleapis.com et fonts.gstatic.com. Chaque affichage de page
# transmettait donc l ADRESSE IP du visiteur a Google LLC, sans son
# consentement et sans qu il puisse s y opposer. Le tribunal de Munich a
# condamne sur ce motif en janvier 2022 et la CNIL retient la meme lecture.
#
# Servies depuis notre propre domaine, les memes polices ne transmettent plus
# rien a personne. La page se charge meme un peu plus vite : deux resolutions
# DNS et deux connexions TLS de moins.
#
# A EXECUTER quand la charte change de police, et pas autrement. Les fichiers
# produits sont deposes par FTP comme le reste.
#
#     perl outils/polices-locales.pl
#
# Il faut `curl`, fourni par Git pour Windows comme par Windows lui-meme.
#
# SEULS LES SOUS-ENSEMBLES latin ET latin-ext sont retenus. Le cyrillique, le
# grec et le vietnamien ne servent a rien ici et pesent pour rien.
# =============================================================================
use strict;
use warnings;

my $DOSSIER = 'public/assets/fonts';
my $CSS     = 'public/assets/css/polices.css';

# Les trois familles, avec les graisses que la charte utilise reellement.
my %FAMILLES = (
    'Lora'      => 'Lora:ital,wght@0,400..700;1,400..700',
    'Quicksand' => 'Quicksand:wght@300..700',
    'Raleway'   => 'Raleway:ital,wght@0,300..900;1,300..900',
);

# Un navigateur recent, sinon Google sert du TTF au lieu du WOFF2.
my $UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
       . '(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';

sub recuperer {
    my ($url, $vers) = @_;
    my @cmd = ('curl', '-sS', '-L', '--max-time', '60', '-A', $UA, $url);
    push @cmd, ('-o', $vers) if $vers;
    open my $p, '-|', @cmd or die "curl : $!";
    local $/;
    my $sortie = <$p>;
    close $p;
    return $sortie;
}

mkdir 'public/assets/fonts' unless -d $DOSSIER;

my $regles = '';
my ($fichiers, $octets) = (0, 0);

for my $nom (sort keys %FAMILLES) {
    my $css = recuperer('https://fonts.googleapis.com/css2?family='
                        . $FAMILLES{$nom} . '&display=swap');
    die "Feuille vide pour $nom.\n" unless $css =~ /\@font-face/;

    # Chaque bloc porte en commentaire le sous-ensemble auquel il repond.
    my $garde = 0;
    for my $bloc (split /(?=\/\*\s*[a-z-]+\s*\*\/)/, $css) {
        next unless $bloc =~ m{/\*\s*([a-z-]+)\s*\*/};
        my $sous = $1;
        next unless $sous eq 'latin' || $sous eq 'latin-ext';
        next unless $bloc =~ m{src:\s*url\((https://[^)]+\.woff2)\)};
        my $url = $1;

        my ($style) = $bloc =~ /font-style:\s*(\w+)/;
        $style //= 'normal';
        my $base = lc($nom) . '-' . $sous . ($style eq 'italic' ? '-italique' : '') . '.woff2';

        recuperer($url, "$DOSSIER/$base");
        my $taille = -s "$DOSSIER/$base" or die "Fichier vide : $base\n";
        # Un WOFF2 commence par « wOF2 ». Une page d erreur enregistree sous ce
        # nom passerait inapercue jusqu au premier affichage.
        open my $f, '<:raw', "$DOSSIER/$base" or die $!;
        read $f, my $signature, 4;
        close $f;
        die "$base n est pas un WOFF2 (signature « $signature »).\n" unless $signature eq 'wOF2';

        printf("  %-34s %6.1f Ko\n", $base, $taille / 1024);
        $fichiers++;
        $octets += $taille;

        my ($poids) = $bloc =~ /font-weight:\s*([^;]+);/;
        my ($plage) = $bloc =~ /unicode-range:\s*([^;]+);/;
        $regles .= "/* $nom · $sous · $style */\n"
                 . "\@font-face {\n"
                 . "  font-family: '$nom';\n"
                 . "  font-style: $style;\n"
                 . "  font-weight: " . ($poids // '400') . ";\n"
                 . "  font-display: swap;\n"
                 . "  src: url('/assets/fonts/$base') format('woff2');\n"
                 . ($plage ? "  unicode-range: $plage;\n" : '')
                 . "}\n\n";
        $garde++;
    }
    die "Aucun bloc latin retenu pour $nom.\n" unless $garde;
}

open my $out, '>:raw', $CSS or die "$CSS : $!";
print $out <<"ENTETE";
/* =============================================================================
   Les polices de la charte, servies depuis notre domaine.

   FICHIER ENGENDRE par outils/polices-locales.pl. Ne pas le modifier a la
   main : changer la liste des familles dans le script et le rejouer.

   Il remplace l appel a fonts.googleapis.com, qui transmettait l adresse IP
   de chaque visiteur a Google LLC sans son consentement. Voir la note
   Espace-client/16-rgpd.md, section 3.1.

   Seuls latin et latin-ext sont embarques. `unicode-range` est conserve : le
   navigateur ne telecharge latin-ext que s il en rencontre un caractere.
   ============================================================================= */

$regles
ENTETE
close $out;

printf("\n%d fichier(s), %.0f Ko au total.\n%s ecrit.\n", $fichiers, $octets / 1024, $CSS);
