* .estilo_1 {color:red;}
*
*
*
*
*
* Si bien es un poco pesada la inclusión de thead y tbody, lo que hago para cumplir con la especificación
* Son opcionales STYLE, THEAD y CAPTION. El sistema sólo reconoce clases, y todas las declaraciones
* deben terminar con ';'.
* Se puede determinar el ancho de las columnas utilizando style='width:x%' en los th, puediendo utilizar em,
* px y %.
* También se permite el uso de colspan, a discreción ;)
*
* Requiere: PEAR_Spreadsheet_Excel, Php.XPath
* @link http://pear.php.net
* @link http://sourceforge.net/projects/phpxpath/
*
* Uso:
*
* $sXhtml="";
* $sFile="nuevo.xls";
* $XHTML2Xls = &XHTML2Xls::get($sXhtml, $sFile);
* $XHTML2Xls->create(); // Crea el archivo
*
*
* @author Claudio Bustos
* @copyrigth Prodem S.A., 2003
* @version 0.1
* @package XML
* @subpackage XHTML2Xls
* @see XHTML2Xls_Expat Implementación Sax del algoritmo, de mayor eficiencia.
* @CVS $Id: XHTML2Xls.class.php,v 2.6 2004/10/27 19:16:17 Administrador Exp $
*/
class XHTML2Xls {
var $sXhtml;
var $oXml;
var $oXls;
var $aXlsFormats = array();
/**
* Función factory, que verifica correción del XHtml
* En caso de error, envía PEAR_Error
* @param string Xml de la(s) tablas
* @param string nombre del archivo
* @return XHTML2Xls
* @throws PEAR_Error
*/
function &get($sXhtml, $sFilename) {
include_once "Spreadsheet/Excel/Writer.php";
include_once("phpxpath/xpath.class.php");
$oXml = new XPath();
$oXml->setVerbose(0);
if (!($res = $oXml->importFromString($sXhtml))) {
return PEAR::raiseError("No se pudo encontrar el archivo con las definiciones");
} else {
$XHTML2Xls = & new XHTML2Xls();
$XHTML2Xls->sXhtml = $sXhtml;
$XHTML2Xls->oXml = &$oXml;
$XHTML2Xls->oXls = new Spreadsheet_Excel_Writer($sFilename);
return $XHTML2Xls;
}
}
/**
* Crea el archivo Excel, en base a la definición de $this->oXhtml
* TODO: verificar cualquier error y lanzar un PEAR_Error en concordancia.
*/
function create() {
$oXml = &$this->oXml;
$oXls = &$this->oXls;
//
////
// parser estilos
////
//
$sStyle = "";
$pStyles = $oXml->match("/xhtml/style");
if ($pStyles) {
foreach($pStyles as $pStyle) {
$sStyle .= $oXml->getData($pStyle);
}
$aRules = $this->parseStyle($sStyle);
foreach($aRules as $sSelector => $aDeclarations) {
if (!preg_match("/\.([A-Za-z0-9_]*)/", $sSelector, $sSelectorName)) {
continue;
}
$sSelectorName = $sSelectorName[1];
$this->aXlsFormats[$sSelectorName] = &$oXls->addFormat();
$oTrans = new Css2XlsFormat();
// MAGIA NEGRA. Ja.
$oTrans->process($this->aXlsFormats[$sSelectorName], $aDeclarations);
}
}
//
////
// parser tabla
////
//
$pTablas = $oXml->match("//table");
$iTablaId = 1;
foreach($pTablas as $pTabla) {
$x = 0; // fila actual
$y = 0; // columna actual
// Loop central. Creo tablas!
$sId = $oXml->getAttributes($pTabla, "id");
if (!$sId) {
$sId = "Tabla_".($iTablaId++);
}
$oHoja = &$oXls->addworksheet($sId);
// Ingreso el título
$pTitulo = $oXml->match("caption[1]", $pTabla);
if ($pTitulo) {
$pTitulo = $pTitulo[0];
$sTitulo = $oXml->getData($pTitulo);
$sClase = $oXml->getAttributes($pTitulo, "class");
$oHoja->write($x++, $y, $sTitulo, $this->getClase($sClase));
}
// Creo la cabecera
$pCabeceras = $oXml->match("thead/th", $pTabla);
if ($pCabeceras) {
foreach($pCabeceras as $pCabecera) {
$sTexto = $oXml->getData($pCabecera);
$sClase = $oXml->getAttributes($pCabecera, "class");
if ($aStyle = $this->parseDeclarations($oXml->getAttributes($pCabecera, "style"))) {
if ($iWidth = $this->getWidth($aStyle)) {
$oHoja->setColumn($y, $y, $iWidth);
}
}
$oHoja->write($x, $y++, $sTexto, $this->getClase($sClase));
}
$x++;
}
// Filas
$pFilas = $oXml->match("tbody/tr", $pTabla);
if ($pFilas) {
foreach($pFilas as $pFila) {
$sFormatoFila = $oXml->getAttributes($pFila, "class");
$y = 0;
// Columnas
$pCols = $oXml->match("td", $pFila);
if ($pCols) {
foreach($pCols as $pCol) {
$sTexto = $oXml->getData($pCol);
$sClase = $oXml->getAttributes($pCol, "class");
$sFormatoCol = $oXml->getAttributes($pCol, "class");
$iColspan = $oXml->getAttributes($pCol, "colspan");
if (!$iColspan) {
$iColspan = 1;
}
if (!$sFormatoCol) {
$sFormatoCol = $sFormatoFila;
}
$oHoja->write($x, $y, $sTexto, $this->getClase($sClase));
if ($iColspan > 1) {
$oHoja->mergeCells($x, $y, $x, $y+$iColspan-1);
}
$y += $iColspan;
}
}
$x++;
}
}
}
$oXls->close();
}
function getWidth($aStyle) {
if ($aStyle["width"]) {
preg_match("/(\d*)(%|em|px)/", $aStyle["width"], $aMatch);
switch($aMatch[2]) {
case '%':
$iWidth = (int)(100 * ($aMatch[1]/XLS_ANCHO_ESTANDAR));
break;
case 'em':
$iWidth = $aMatch[1];
break;
case 'px':
$iWidth = (int)($aMatch[1]/7);
break;
}
return $iWidth;
} else {
return false;
}
}
function &getClase($sClase) {
if (isset($this->aXlsFormats[$sClase])) {
return $this->aXlsFormats[$sClase];
} else {
return false;
}
}
function parseStyle($sStyle) {
if (!$sStyle) {
return false;
}
$aOut = array();
preg_match_all("/\s*?(.*?)\s*?\{(.*?)\}/smi", $sStyle, $aRules, PREG_SET_ORDER);
foreach($aRules as $aRule) {
$sSelector = trim($aRule[1]);
$sDeclarations = trim($aRule[2]);
$aOut[$sSelector] = $this->parseDeclarations($sDeclarations);
}
return $aOut;
}
function parseDeclarations($sDeclarations) {
if (substr(trim($sDeclarations), -1) != ";") {
$sDeclarations = trim($sDeclarations).";";
}
preg_match_all("/\s*(.*?)\s*:\s*(.*?)\s*;/", $sDeclarations, $aDeclarations, PREG_SET_ORDER);
$aOut = array();
foreach($aDeclarations as $aDeclaration) {
$sDeclarationName = $aDeclaration[1];
$sDeclarationValue = $aDeclaration[2];
$aOut[$sDeclarationName] = $sDeclarationValue;
}
return $aOut;
}
}
/**
* @class Css2XlsFormat
*/
/**
* Clase que transforma a un Spreadsheet_Excel_Writer_Format
* en base a un array de declaraciones CSS.
* Está en una clase aparte, ya que se requiere una cantidad muy grande de funciones, lo
* cual entorpecería el manejo de la clase padre.
* @package XML
* @subpackage XHTML2Xls
*/
class Css2XlsFormat {
/**
* Tabla de funciones, a ser gatilladas por cada declaración.
*
* @var array
*/
var $_aTransformations = array(
"text-align" => "setAlign",
"background" => "setBgColor",
"color" => "setFgColor",
"font-weigth" => array("sLocal" => "setFontWeigth", "sRemota" => "setBold"),
"font-family" => array("sLocal" => "setFontFamily", "sRemota" => "setFontFamily"),
"text-style" => array("sLocal" => "setTextStyle", "sRemota" => "setItalic"),
"font-size" => array("sLocal" => "setFontSize", "sRemota" => "setSize"));
function process(&$oFormat, $aDeclarations) {
foreach($aDeclarations as $sName => $sValue) {
if (!isset($this->_aTransformations[$sName])) {
continue;
}
$mFuncion = $this->_aTransformations[$sName];
if (is_array($mFuncion)) {
$mRes = call_user_func(array("Css2XlsFormat", $mFuncion["sLocal"]), $sValue);
if (PEAR::isError($mRes)) {
trigger_error($sValue->getMessage(), E_USER_WARNING);
continue;
} elseif($mRes === FALSE) {
continue;
} elseif(is_array($mRes)) {
$sNombreFuncion = $mRes["sFuncion"];
$sValor = $mRes["mValor"];
} else {
$sNombreFuncion = $mFuncion["sRemota"];
$sValue = $mRes;
}
} else {
$sNombreFuncion = $mFuncion;
}
call_user_func(array(&$oFormat, $sNombreFuncion), $sValue);
}
}
function setFontWeigth($sValue) {
if (strpos($sValue, "bold") !== FALSE) {
return 700;
} else {
return false;
}
}
function setFontSize($sValue) {
preg_match("/(\d+)(pt)/", $sValue, $match);
if ($match) {
return $match[1];
} else {
return false;
}
}
function setFontFamily($sValue) {
$aFonts = explode(",", $sValue);
$sFamily = trim(reset($aFonts));
return $sFamily;
}
function setTextStyle($sValue) {
if (strpos($sValue, "italic") !== FALSE) {
return true;
} else {
return false;
}
}
}
?>