paolo@bimodesign.com | +34 608 61 64 10

Framework

        

Da XML a PDF

Descrivo schematicamente come produrre un documento pdf, partendo da due strutture dati XML (entrambe archiviate in una base dati MySQL) e usando la classe gratuita FPDF.

Le strutture XML

La prima struttura XML, identifica la struttura del template, e non cambia, se non cambiano chiaramente le specifiche o se si applica ad un altro formato.

E' formata da 3 elementi principali.

- Border: Per disegnare i bordi delle varie sezioni e presenta i seguenti elementi

<pos_x>9</pos_x>
<pos_y>25</pos_y>
<width>192</width>
<height>7</height>
<draw_color>0,0,0</draw_color>

- Label: Il testo che non cambiera' durante l'elaborazione.
(in futuro verra' anche questo reso parametrico, per permettere, ad esempio, le varie versioni per ogni linguaggio)

<pos_x>98</pos_x>
<pos_y>26</pos_y>
<font_style>Arial</font_style>
<font_size>14</font_size>
<font_align>L</font_align>
<font_weight>B</font_weight>
<font_color>0,0,0</font_color>
<data>Customer ID:</data>

- Input: Il testo che potra' cambiare.

<pos_x>165</pos_x>
<pos_y>53</pos_y>
<width>4</width>
<font_style>Arial</font_style>
<font_size>8</font_size>
<font_align>L</font_align>
<font_weight></font_weight>
<font_color>0,0,0</font_color>
<font_space>0</font_space>
<background_color>255,255,255</background_color>
<border_input>1</border_input>
<data_complete></data_complete>
<data_value>LAST_NAME</data_value>

"data_complete" potra' o meno contenere un testo. Se cosi' fosse dovra' contenere il valore presente in data_value e quindi, secondo l'esempio, una cosa del genere "asd qwerty LAST_NAME zxcv"

Il "data_value" fara' riferimento alla seconda struttura XML, creata di volta in volta per ogni utente per il quale generiamo il documento.

La struttura e' semplice e si presenta cosi'

<LAST_NAME>test_contract_28</LAST_NAME>

Il codice php

Tralascio la spiegazione di come e' strutturata la classe di generazione del pdf, e mi soffermo solo su come gestisco la scrittura sul pdf dei campi variabili (Il disegno dei bordi e le label sono di una semplicita' disarmante.)

Innanzitutto leggo le due strutture con la SimpleXMLElement($xmlStr) e faccio un semplice ciclo di questo tipo.

$xmlStr  = new SimpleXMLElement($xmlStr);
$xmlData = new SimpleXMLElement($xmlData);

foreach ($xmlStr->pages->page as $page) {
	for($iOccurrence=1; $iOccurrence <= $page->occurrence; $iOccurrence+=1){
		$pdf->AddPage();
		$pdf->Image(_PATH_MAIN.'common/languages/056/en/images/logo_cw2.jpg',9,10);
		$code = new pdfbarcode128($iAccountIdMain, 2 );
		$code->set_pdf_document($pdf);
		$code->draw_barcode(176, 12, 10, true );

		... Section ...
		... Label ...

		// Print all the inputs
		foreach ($page->inputs->input as $input) {
			$this->printInput($pdf,
			$input->pos_x,
			$input->pos_y,
			$input->width,
			$input->font_style,
			$input->font_size,
			$input->font_align,
			$input->font_weight,
			$input->font_color,
			$input->font_space,
			$input->background_color,
			$input->border_input,
			$input->data_complete,
			$input->data_value,
			$xmlData);
		}
	}
}

// Output
$pdf->Output();

Nota: aggiungo anche il parametro page, che uso per sapere quante volte va generata una pagina e la generazione del barcode

Il metodo printInput(), che riceve input anche la seconda struttura XML, quella dei dati, presenta un codice di questo tipo.

private function printInput($pdf,$iPosX,$iPosY,$iWidth,$sFontStyle,$iFontSize,$sFontAlign,$sFontWeight,$sFontColor,$iFontSpace,$sBackgroundColor,$iBorderInput,$sDataComplete,$sDataValue,&$xmlData){
	// iFontSpace   1: yes - 0: no
	// iBorderInput 1: yes - 0: no
	// sDataComplete null or string to substitute
		
	// Insert data into a string. If it's needed.
	if(!strlen($sDataComplete)){
		$sStringFinal=$xmlData->{$sDataValue};
	}else{
		$sStringFinal=str_replace($sDataValue,$xmlData->{$sDataValue},$sDataComplete);
	}
		
	// Put the space into the character. If it's needed.
	if($iFontSpace==0){
		$sDataFormatFinal=$sStringFinal;
	}else{
		$sDataFormatFinal=preg_replace('`.(?!\z)`s','$0 ',$sStringFinal);
	}
				
	$pdf->SetFont($sFontStyle,$sFontWeight,$iFontSize);
	$pdf->SetLineWidth(1/4);
	$iHeight=5;

	$pdf->SetDrawColor(0,0,0);

	$sRGBBackgroundColor = explode(",", $sBackgroundColor);
	$pdf->SetFillColor($sRGBBackgroundColor[0],$sRGBBackgroundColor[1],$sRGBBackgroundColor[2]);

	$sRGBFontColor = explode(",", $sFontColor);
	$pdf->SetTextColor($sRGBFontColor[0],$sRGBFontColor[1],$sRGBFontColor[2]);
		
	$pdf->SetXY(floatval($iPosX), floatval($iPosY));

	//Use iconv function because is the euro char
	$pdf->MultiCell($iWidth,$iHeight,iconv("UTF-8", "CP1252",$sDataFormatFinal),$iBorderInput,1,$sFontAlign,true);		
	$pdf->Ln(4);
}		

Notare, in particolare, come recupero il valore dalla struttura dati

$sStringFinal=$xmlData->{$sDataValue};