PHP: una classe per generare un calendario stampabile in PDF

screenshot del calendarioQuesta classe PHP genera un calendario gregoriano in PDF, disponendo ogni mese in una pagina con l’elenco dei giorni feriali e delle festività. E’ possibile scegliere l’anno e cambiare il logo che compare in testa alla pagina.

Si avvicina l’anno nuovo e quasi tutti siamo alla ricerca di calendari. Sebbene molto spesso ci vengono regalati a scopo pubblicitario, una buona parte di questi calendari sono poco utili. Infatti, troppo spesso si tende a privilegiare l’aspetto estetico, a corredo grandi immagini, belle per carità, ma poi i giorni sono scritti piccoli che ci vuole la lente per leggerli. Oppure sono pieni zeppi di informazioni tanto inutili quanto ingombranti, di nomi di santi assolutamente improponibili per gli onomastici dei viventi di questa era, ed ancora, così minimalisti che non sono segnate nemmeno le festività nazionali.

Per questi motivi, ed anche per imparare qualcosa di più sulle funzioni di data e ora in PHP, ho pensato di scrivere una classe in grado di generare il mio calendario da appendere in cucina!

Il codice è abbastanza versatile: genera un’array multidimensionale con i mesi in lettere, i giorni in numero, i giorni della settimana, e un flag che indica se il giorno corrisponde ad una festività oppure no. La classe estende le lbrerie PDF R&OS, in questo modo è sufficiente istanziare una sola classe per generare anche il PDF.

Ecco il codice:

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
<?php 
require_once("class.ezpdf.php");
 
class calendarPDF extends Cezpdf {
 
    var $calArray = array();
    var $yearNum = NULL;
    var $logo = NULL;
    var $easterDay = array();
 
    function buildCalendar($yearYouWant,$logoPath){
      $this->yearNum = empty($yearYouWant) ? intval(date("Y")) : $yearYouWant;
      $this->logo = $logoPath;
      $e = strtotime("$yearYouWant-03-21 +".
                            easter_days($yearYouWant)." days");
      $this->easterDay = array("month"=>intval(date("n",$e)), 
                               "day"=>intval(date("j",$e)));
 
      for ($i=1;$i<=12;$i++) {
        $this->buildMonths($i);        
      }
    }
 
   function buildMonths ($monthNum)
   {
    $giorniSettimana = array("Domenica","Lunedì","Martedì","Mercoledì",
                             "Giovedì","Venerdì","Sabato");
    $mesiAnno = array("GENNAIO","FEBBRAIO","MARZO","APRILE","MAGGIO",
                      "GIUGNO","LUGLIO","AGOSTO","SETTEMBRE","OTTOBRE",
                      "NOVEMBRE","DICEMBRE");                              
    $firstDay = mktime(0,0,0,$monthNum,1,$this->yearNum);
    $daysPerMonth = intval(date("t",$firstDay));
    $monthName = $mesiAnno[$monthNum-1];
    for ($i = 1; $i<=$daysPerMonth; $i++)
    {
      $actualDay = mktime(0,0,0,$monthNum,$i,$this->yearNum);
      $numDay = intval(date("w",$actualDay));
 
      if ($numDay == 0 ) {
        $f = true;
      }
      elseif ($this->isHoliday($monthNum,date("j",$actualDay))) {
          $f = true;
        }
      elseif (($monthNum == $this->easterDay['month']) && 
                (intval(date("j",$actualDay)) == $this->easterDay['day']+1)) {
          $f = true;
      }
      else {
          $f = false;
      }
 
      $this->calArray[$monthName][] = array("day"=>date("d",$actualDay), 
                                      "weekday"=>iconv('UTF-8','CP1252',
                                      $giorniSettimana[$numDay]),
                                      "F"=>$f);                          
    }   
   }
 
   function isHoliday ($monthNumber,$dayNumber)
   {
    $monthNumber = intval($monthNumber);
    $dayNumber = intval($dayNumber);
    $holiday = array(1=>array(1,6), 
                     4=>array(25),
                     5=>array(1),
                     6=>array(2),
                     8=>array(15),
                     11=>array(1),
                     12=>array(8,25,26)                                      
                     );
     $holidayKeys = array_keys($holiday);
     if (in_array($monthNumber,$holidayKeys)){
       if (in_array($dayNumber,$holiday[$monthNumber])) {
        return true;
       }
     }
     return false;
   }
}
?>

Per ottenere il testo in italiano, ho scelto di non utilizzare le funzioni setlocale() e strftime() perché, come viene spiegato in questo post, a seconda del sistema operativo impiegato, cambia la stringa per l’impostazione della lingua, passata come secondo parametro a setlocale(). Per ovviare a questo fastidioso comportamento, ho preferito costruire due array, uno per i nomi dei mesi ed uno per i nomi dei giorni della settimana.
Per la determinazione delle festività ho impostato in un array le date nazionali italiane, ovvero

  • 1 e 6 gennaio
  • 25 aprile
  • 1 maggio
  • 2 giugno
  • 15 agosto
  • 1 novembre
  • 8, 25 e 26 dicembre

Per ottenere, invece, la festività del lunedì dopo Pasqua, è necessario calcolare la data della Pasqua che varia in dipendenza del ciclo lunare e del tipo di calendario (noi utilizziamo il gregoriano, gli ortodossi il giuliano). Chi ha voglia di saperne di più sull’algoritmo di calcolo può fare un’approfondimento qui, chi invece è pigro (come me) può ricorrere alle funzioni PHP easter_date() o easter_days() che restituiscono rispettivamente la data della Paqua in UTS (valida solo fra il 1970 e il 2037) e il numero di giorni dopo il 21 marzo dell’anno in corso.
Poiché easter_date() risulta essere affetta da un bug riportato qui, ho preferito usare easter_days() che riporta sempre date corrette.

Ecco infine il codice per generare il calendario in PDF:

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
<?php
    require_once("calendario_ita.php");
 
    $cal = new calendarPDF('a4','portrait');
    $cal->buildCalendar(2009,"./logo.png");
    $cal->selectFont('./fonts/Helvetica.afm');
    $tmp = array(
      'b'=>'Helvetica-Bold.afm'
      ,'i'=>'Helvetica-Oblique.afm'
      ,'bi'=>'Helvetica-BoldOblique.afm'
      ,'ib'=>'Helvetica-BoldOblique.afm'
      ,'bb'=>'Helvetica-Bold.afm'
    );
    $cal->setFontFamily("Helvetica.afm",$tmp);
    $cal->ezSetMargins(30,20,60,30);
 
    foreach ($cal->calArray as $calMonth => $dati) {
      $cal->ezImage($cal->logo,0,490,"none","left");
      $cal->setColor(0,0,1,0);
      $monthString = "<b>".$calMonth." ".$cal->yearNum."</b>";
      $cal->ezSetY(700);
      $cal->ezText($monthString,18);
      $cal->ezSetDY(-10);
      $cal->setColor(0,0,0);
      foreach ($dati as $singleDay) {
        $toPrint = $singleDay['day']."  ".$singleDay['weekday'];
        if ($singleDay['F']) {
            $toPrint = "<b>".$toPrint."</b>";
            $cal->setColor(1,0,0,0);
        }   
        $y = $cal->ezText($toPrint,14);
        $cal->setColor(0,0,0,0);
        $cal->setLineStyle(1);
        $cal->line(155,$y,540,$y);
        $y = $cal->ezSetDY(-4);
      }
      $cal->ezNewPage();
    }
    $cal->stream();
?>

Se volete solo stampare il calendario cliccate qui, se invece siete interessati anche al codice, in questo pacchetto è disponibile la classe (compresa la libreria R&OS) e il codice per gli esempi.

Conclusioni

Semplice, se nessuno mi regala un calendario… me lo stampo da me!!

Riferimenti ed approfondimenti: