* .estilo_1 {color:red;} * * * * * * * *
Titulo
th1
*
* * 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; } } } ?>