<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/perl

#--------------------------------------------------------------------
# Fichero:  curso.pl
# Objetivo: Gestionar el curso
# Autor:    Pedro Reina &lt;pedro@pedroreina.org&gt;
# Fecha:    S.18.9.2004
#--------------------------------------------------------------------

#---------------------------------
# Declaración de módulos
#---------------------------------

use strict;                 # Comprobaciones estrictas
use Unicode::String;        # Para pasar de Unicode a Latin1
use XML::Parser;            # Para leer archivos XML

#---------------------------------
# Declaración de variables
# Ajústalas en tu sistema
#---------------------------------

# Directorio de instalación general de OpenOffice.org
my $DirectorioOOo = "/opt/openofficeorg/";

# Directorio base de trabajo del curso 2004-2005
my $DirectorioCurso = "/home/pedro/dat/curs0405/";

# Tras arrancar OOo aún hay que esperar unos segundos más
my $EsperaOOo = 3;

# Se puede arrancar OpenOffice.org en modo invisible o no
# Esta varible puede ser 0 (no) o 1 (sí)
my $OOoInvisible = 1;

# Para las copias de seguridad en otra unidad
# (Utiliza rsync en local)
my $UnidadCopiaExt = "/mnt/zip";
my $DirectorioCopiaExt = "$UnidadCopiaExt/curs0405";

# Para las copias en el servidor web
# (Utiliza rsync mediante ssh)
my $DirectorioCopiaWeb = 'web@tedecea:curs0405';

#---------------------------------
# Declaración de variables
# Organización interna del curso
#---------------------------------

my $Descripcion = $DirectorioCurso . "original/curs0405.xml";
my $OrdenZip = "zip -9 -q -r -u";

my $ExtensionTexto = "sxw";  # Extensión de los archivos de texto de OOo
my $ExtensionDibujo = "sxd"; # Extensión de los archivos de dibujo de OOo

my $DirectorioClasesOOo = $DirectorioOOo . "program/classes/";
my $Impresora = "Generic Printer (T42 enabled)";

#---------------------------------
# Declaración de variables
# Estas son internas del programa
#---------------------------------

my $Narg = 0;               # Número de argumentos
my $Opcion = "";            # Opción pedida al programa

my @Libro;                  # El array de partes del libro
my %Parte;                  # El hash con cada parte del libro
my @Contenido;              # El array de hojas de una parte
my %Hoja;                   # El hash de cada hoja

my $TotalPartes = 0;        # Contador de partes del curso
my $TotalHojas = 0;         # Contador de hojas del curso

my $Analizador;             # Analizador de archivos XML
my $Profundidad = 0;        # Para saber a qué profundidad del XML se está
my $Punto = 0;              # Para saber en qué punto del XML se está

my @ListaOpciones;          # Manejo de las opciones de la línea de órdenes

#---------------------------------
# Definición de constantes
#---------------------------------

# Para la variable $Profundidad
use constant PARTE  =&gt; 1;
use constant HOJA   =&gt; 2;

# Para la variable $Punto
use constant NOMBRE =&gt; 1;
use constant CLAVE  =&gt; 2;
use constant TIPO   =&gt; 3;
use constant BLANCA =&gt; 4;

#---------------------------------
# Programa principal
#---------------------------------

# Presentación
print "\nInformática Curso 2004-2005";
print "\n===========================\n";

# Rellenamos @ListaOpciones
PreparaOpciones();

# Si no nos mandan ninguna opción, informamos de las disponibles
$Narg = scalar(@ARGV);
if ( ! $Narg )
  { Ayuda(); }

else
  {
  # Vemos qué nos han pedido
  $Opcion = $ARGV[0];

  my $Visto = 0;
  for ( my $i=0 ; $i &lt; scalar(@ListaOpciones) ; $i++ )
    {
    # Vemos una opción válida
    if ( $ListaOpciones[$i]{'opcion'} eq $Opcion )
      {
      $Visto = 1;

      # Analizamos el archivo de descripción y rellenamos @Libro
      Analiza();

      # Ejecutamos la opción
      my $Funcion = $ListaOpciones[$i]{'funcion'};
      &amp;$Funcion();
      }
    }

  # Si no se encuentra la opción, informamos del error
  if ( ! $Visto )
    {
    print "\nError: la opción \"$Opcion\" no es válida\n";
    Ayuda();
    }
  }

# Despedida
print "\n\nTerminado.\n";

#---------------------------------
# Funciones
#---------------------------------

sub Ayuda
  {
  my $i = 0, my $j = 0;

  print "\nOpciones disponibles:";

  # Para que luego quede más agradable la impresión de las
  # opciones, calculamos la longitud de cada una
  my $Max = 0;
  for ( $i=0 ; $i &lt; scalar(@ListaOpciones) ; $i++ )
    {
    my $Longitud = length($ListaOpciones[$i]{'opcion'});
    if ( $Longitud &gt; $Max )
      { $Max = $Longitud; }
    }

  # Damos dos espacios tras las opciones
  $Max += 2;

  # Imprimimos las opciones y su explicación
  for ( $i=0 ; $i &lt; scalar(@ListaOpciones) ; $i++ )
    {
    print ("\n  $ListaOpciones[$i]{'opcion'}");
    for ( $j = length($ListaOpciones[$i]{'opcion'}) ; $j &lt; $Max ; $j++ )
      { print (" "); }
    print ("$ListaOpciones[$i]{'texto'}");
    }
  }

#---------------------------------

sub PreparaOpciones
  {
  @ListaOpciones =
    (
      {
      opcion  =&gt; "info",
      funcion =&gt; \&amp;Info,
      texto   =&gt; "muestra información del curso"
      },

      {
      opcion  =&gt; "lista",
      funcion =&gt; \&amp;Lista,
      texto   =&gt; "muestra el listado de partes y hojas"
      },

      {
      opcion  =&gt; "listaweb",
      funcion =&gt; \&amp;ListaWeb,
      texto   =&gt; "muestra el listado en formato HTML"
      },

      {
      opcion  =&gt; "arrancaooo",
      funcion =&gt; \&amp;ArrancaOOo,
      texto   =&gt; "arranca OpenOffice.org en modo receptor"
      },

      {
      opcion  =&gt; "generaps",
      funcion =&gt; \&amp;GeneraPS,
      texto   =&gt; "genera todos los archivos PostScript"
      },

      {
      opcion  =&gt; "paraooo",
      funcion =&gt; \&amp;ParaOOo,
      texto   =&gt; "termina la ejecución de OpenOffice.org"
      },

      {
      opcion  =&gt; "generapdf",
      funcion =&gt; \&amp;GeneraPDF,
      texto   =&gt; "genera los archivos PDF de cada hoja"
      },

      {
      opcion  =&gt; "partes",
      funcion =&gt; \&amp;Partes,
      texto   =&gt; "crea los PDF finales de cada parte"
      },

      {
      opcion  =&gt; "zipartes",
      funcion =&gt; \&amp;ZipPartes,
      texto   =&gt; "comprime en zip los PDF de cada parte"
      },

      {
      opcion  =&gt; "libro",
      funcion =&gt; \&amp;Libro,
      texto   =&gt; "crea el PDF final del libro"
      },

      {
      opcion  =&gt; "ziplibro",
      funcion =&gt; \&amp;ZipLibro,
      texto   =&gt; "comprime en zip el PDF del libro"
      },

      {
      opcion  =&gt; "haztodo",
      funcion =&gt; \&amp;HazTodo,
      texto   =&gt; "genera todos los archivos del proyecto"
      },

      {
      opcion  =&gt; "limpia",
      funcion =&gt; \&amp;Limpia,
      texto   =&gt; "borra todos los archivos generados"
      },
      {
      opcion  =&gt; "salva",
      funcion =&gt; \&amp;Salva,
      texto   =&gt; "realiza una copia de seguridad"
      },

      {
      opcion  =&gt; "recopila",
      funcion =&gt; \&amp;RecopilaOriginales,
      texto   =&gt; "recopila todos los originales"
      },

      {
      opcion  =&gt; "copiaext",
      funcion =&gt; \&amp;CopiaExt,
      texto   =&gt; "copia archivos a una unidad externa"
      },

      {
      opcion  =&gt; "copiaweb",
      funcion =&gt; \&amp;CopiaWeb,
      texto   =&gt; "copia archivos a un servidor"
      },

      {
      opcion  =&gt; "salvacopia",
      funcion =&gt; \&amp;SalvaCopia,
      texto   =&gt; "salva los originales y los copia"
      },
    );
  }

#---------------------------------

sub Info
  {
  my $i, my $j;

  print "\nEl directorio del curso es $DirectorioCurso";
  print "\nEl archivo de descripción es $Descripcion";

  my $TotalPartes = 0;
  my $TotalHojas = 0;

  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    $TotalPartes++;
    for ( $j=0 ; $j &lt; scalar(@{$Libro[$i]{'contenido'}}) ; $j++ )
      { $TotalHojas++; }
    }

  print "\nPartes: $TotalPartes. Hojas: $TotalHojas";
  }

#---------------------------------

sub Lista
  {
  my $i, my $j;

  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    print "\n\n$Libro[$i]{'nombre'} -&gt; ";
    print "$Libro[$i]{'clave'}";
    for ($j=0 ; $j &lt; scalar(@{$Libro[$i]{'contenido'}}) ; $j++ )
      {
      print "\n  $Libro[$i]{'contenido'}[$j]{'nombre'} -&gt; ";
      print "$Libro[$i]{'contenido'}[$j]{'clave'}";
      print " $Libro[$i]{'contenido'}[$j]{'blanca'}";
      }
    }
  }

#---------------------------------

sub ListaWeb
  {
  my $i, my $j;

  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    print "\n&lt;P&gt;&lt;B&gt;$Libro[$i]{'nombre'}&lt;/B&gt;";
    print "\n&lt;OL&gt;";
    for ($j=0 ; $j &lt; scalar(@{$Libro[$i]{'contenido'}}) ; $j++ )
      { print "\n  &lt;LI&gt;$Libro[$i]{'contenido'}[$j]{'nombre'}"; }
    print "\n&lt;/OL&gt;\n";
    }
  }

#---------------------------------

sub Limpia
  {
  my $Limpiar = "rm -f " . $DirectorioCurso;
  my $Orden = "";

  # Los archivos PostScript
  $Orden = $Limpiar . "postscript/*";
  print ("\nBorrando archivos PostScript...");
  system ($Orden);
  print " hecho.";

  # Los archivos PDF de las hojas
  $Orden = $Limpiar . "hojas/*";
  print ("\nBorrando archivos PDF de las hojas...");
  system ($Orden);
  print " hecho.";

  # Los archivos PDF del libro y las partes
  $Orden = $Limpiar . "libro/*";
  print ("\nBorrando archivos PDF del libro y las partes...");
  system ($Orden);
  print " hecho.";

  # Los archivos zip resultantes
  $Orden = $Limpiar . "resultado/*";
  print ("\nBorrando archivos zip resultantes...");
  system ($Orden);
  print " hecho.";

  }

#---------------------------------

sub HazTodo
  {
  ArrancaOOo();
  GeneraPS();
  ParaOOo();
  GeneraPDF();
  Partes();
  ZipPartes();
  Libro();
  ZipLibro();
  }

#---------------------------------

sub SalvaCopia
  {
  Salva();
  RecopilaOriginales();
  CopiaExt();
  CopiaWeb();
  }

#---------------------------------

sub Salva
  {
  my $i, my $j;
  my $TotalPartes = 0;

  print "\nSalvando...";
  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    $TotalPartes++;
    $TotalHojas = 0;
    print "\n$TotalPartes: ";
    for ( $j=0 ; $j &lt; scalar(@{$Libro[$i]{'contenido'}}) ; $j++ )
      {
      $TotalHojas++;
      print " $TotalHojas";

      # Copia de seguridad de la hoja
      my $Directorio = $Libro[$i]{'clave'};
      my $NombreFisico = $Libro[$i]{'contenido'}[$j]{'clave'};
      SalvaArchivo ($Directorio, $NombreFisico);

      # Si hay reverso con hoja blanca, también hay que salvarla
      $NombreFisico = $Libro[$i]{'contenido'}[$j]{'blanca'};
      if ( length ($NombreFisico) )
        { SalvaArchivo ($Directorio, $NombreFisico); }
      }
    }
  print "\nHecho.";
  }

#---------------------------------

sub RecopilaOriginales
  {
  my $i;

  print "\nRecopilando originales...";
  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    my $NombreParte = $Libro[$i]{'nombre'};
    my $ClaveParte = $Libro[$i]{'clave'};

    print "\n$NombreParte... ";
    my $Orden = "cd $DirectorioCurso" . "original/;" .
                $OrdenZip . " " .
                $DirectorioCurso . "resultado/$ClaveParte-ooo.zip " .
                $ClaveParte;
    system ($Orden);
    print " hecho.";
    }
  print "\nHecho.";
  }

#---------------------------------

sub ZipPartes
  {
  my $i;

  print "\nComprimiendo partes...";
  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    my $NombreParte = $Libro[$i]{'nombre'};
    my $ClaveParte = $Libro[$i]{'clave'};

    print "\n$NombreParte... ";
    my $Origen = $DirectorioCurso . "libro/$ClaveParte.pdf";
    my $Destino = $DirectorioCurso . "resultado/$ClaveParte-pdf.zip";
    if ( NecesitaActualizar ($Origen, $Destino) )
      {
      my $Orden = "cd $DirectorioCurso" . "libro/;" .
                  "$OrdenZip $Destino $ClaveParte.pdf";
      system ($Orden);
      print " hecho.";
      }
    else
      { print " actualizado."; }
    }
  print "\nHecho.";
  }

#---------------------------------

sub ZipLibro
  {
  print "\nCreando libro.zip... ";
  my $Orden = "cd $DirectorioCurso" . "libro/;" .
              $OrdenZip . " " .
              $DirectorioCurso . "resultado/libro.zip " .
              "libro.pdf";
  system ($Orden);
  print " hecho.";
  }

#---------------------------------

sub SalvaArchivo
  {
  # Tomamos los parámetros
  my ($Directorio, $NombreFisico) = @_;

  my $Orden = "cd $DirectorioCurso" . "original/;" .
              $OrdenZip . " " .
              $DirectorioCurso . "seguridad/$NombreFisico.zip " .
              $Directorio . "/" . $NombreFisico;
  system ($Orden);
  }

#---------------------------------

sub ArrancaOOo
  {
  my $Orden = $DirectorioOOo . "program/soffice ";
  if ( $OOoInvisible )
    { $Orden .= "-invisible "; }
  $Orden .= '"-accept=socket,host=localhost,port=8100;urp;"&amp;';

  print "\nArrancando OpenOffice.org...";
  system ($Orden);

  # Comprobamos si ya ha arrancado
  my $Arrancado = 0;
  while ( ! $Arrancado )
    {
    my $ProcesosOOo = qx (ps aux | grep soffice | wc -l);
    $Arrancado = $ProcesosOOo &gt; 6;
    }

  # Y además, hay que esperar un poco porque si no no acepta conexiones
  sleep ($EsperaOOo);

  print "\nHecho.";
  }

#---------------------------------

sub ParaOOo
  {
  my $Orden = 'killall soffice.bin';
  print "\nParando OpenOffice.org...";
  system ($Orden);
  print "\ hecho.";
  }

#---------------------------------

sub GeneraPS
  {
  my $i, my $j;

  my $TotalPartes = 0;
  my $Origen = "";
  my $Destino = "";

  print "\nGenerando PS...";
  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    $TotalPartes++;
    $TotalHojas = 0;
    print "\n$TotalPartes: ";
    for ( $j=0 ; $j &lt; scalar(@{$Libro[$i]{'contenido'}}) ; $j++ )
      {
      $TotalHojas++;
      print " $TotalHojas";

      my $Directorio = $Libro[$i]{'clave'};
      my $Nombre = $Libro[$i]{'contenido'}[$j]{'clave'};

      my $Extension = "";
      if ( $Libro[$i]{'contenido'}[$j]{'tipo'} eq 'texto' )
        { $Extension = $ExtensionTexto; }
      if ( $Libro[$i]{'contenido'}[$j]{'tipo'} eq 'dibujo' )
        { $Extension = $ExtensionDibujo; }

      $Origen = $DirectorioCurso . "original/" . $Directorio;
      $Origen .= "/" . $Nombre . "/" . $Nombre . "." . $Extension;

      $Destino = $DirectorioCurso . "postscript/" . $Nombre . ".ps";

      if ( NecesitaActualizar ($Origen, $Destino) )
        { CreaPS ($Origen, $Destino); }

      # Si hay reverso con hoja blanca, también hay que generar su PS
      $Nombre = $Libro[$i]{'contenido'}[$j]{'blanca'};
      if ( length ($Nombre) )
        {
        my $Origen = $DirectorioCurso . "original/" . $Directorio;
        $Origen .= "/" . $Nombre . "/" . $Nombre . "." . $ExtensionDibujo;

        my $Destino = $DirectorioCurso . "postscript/" . $Nombre . ".ps";

        if ( NecesitaActualizar ($Origen, $Destino) )
          { CreaPS ($Origen, $Destino); }
        }
      }
    }
  print "\nHecho.";
  }

#---------------------------------

sub GeneraPDF
  {
  my $i, my $j;

  my $TotalPartes = 0;

  print "\nGenerando hojas PDF...";
  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    $TotalPartes++;
    $TotalHojas = 0;
    print "\n$TotalPartes: ";
    for ( $j=0 ; $j &lt; scalar(@{$Libro[$i]{'contenido'}}) ; $j++ )
      {
      $TotalHojas++;
      print " $TotalHojas";

      my $Nombre = $Libro[$i]{'contenido'}[$j]{'clave'};
      my $Origen = $DirectorioCurso . "postscript/" . $Nombre . ".ps";

      my $Blanca = $Libro[$i]{'contenido'}[$j]{'blanca'};
      if ( length ($Blanca) )
        {
        $Origen .= " " . $DirectorioCurso . "postscript/" . $Blanca . ".ps";
        }

      my $Destino = $DirectorioCurso . "hojas/" . $Nombre . ".pdf";

      if ( NecesitaActualizar ($Origen, $Destino) )
        { CreaPDF ($Origen, $Destino); }
      }
    }
  print "\nHecho.";
  }

#---------------------------------

sub CreaPS
  {
  my $ClassPath = "";

  # Tomamos los parámetros
  my ($Origen, $Destino) = @_;

  # Preparamos el ClassPath
  $ClassPath .= $DirectorioClasesOOo . "jurt.jar:";
  $ClassPath .= $DirectorioClasesOOo . "unoil.jar:";
  $ClassPath .= $DirectorioClasesOOo . "ridl.jar:";
  $ClassPath .= $DirectorioClasesOOo . "sandbox.jar:";
  $ClassPath .= $DirectorioClasesOOo . "juh.jar:";
  $ClassPath .= $DirectorioCurso . "bin";

  # Preparamos la invocación a imprimeOOoPS.class
  my $Orden = "java -classpath \"$ClassPath\" imprimeOOoPS " .
              "\"$Impresora\" $Origen $Destino $OOoInvisible";

  # La ejecutamos
  system ($Orden);
  }

#---------------------------------

sub CreaPDF
  {
  # Tomamos los parámetros
  my ($Origen, $Destino) = @_;

  # Preparamos la invocación a gs
  my $Orden = "gs -r600 -sDEVICE=pdfwrite "                            .
     " -dColorImageFilter=/FlateEncode -dAutoFilterColorImages=false " .
     " -dGrayImageFilter=/FlateEncode  -dAutoFilterGrayImages=false  " .
     " -dMonoImageFilter=/FlateEncode "                                .
     " -sOutputFile=$Destino -q -dNOPAUSE $Origen -c quit";

  # La ejecutamos
  system ($Orden);
  }

#---------------------------------

sub Partes
  {
  my $i, my $j;

  print "\nCreando PDF de partes...";

  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    my $NombreParte = $Libro[$i]{'nombre'};
    my $ClaveParte = $Libro[$i]{'clave'};

    print "\n$NombreParte... ";

    my $Origen = "";
    my $Destino = $DirectorioCurso . "libro/" . $ClaveParte . ".pdf";

    for ( $j=0 ; $j &lt; scalar(@{$Libro[$i]{'contenido'}}) ; $j++ )
      {
      my $Clave = $Libro[$i]{'contenido'}[$j]{'clave'};
      my $Blanca = $Libro[$i]{'contenido'}[$j]{'blanca'};
      $Origen .= $DirectorioCurso . "postscript/" . $Clave . ".ps ";
      if ( length ($Blanca) )
        { $Origen .= $DirectorioCurso . "postscript/" . $Blanca . ".ps "; }
      }

    if ( NecesitaActualizar ($Origen, $Destino) )
      {
      CreaPDF ($Origen, $Destino);
      print " hecho.";
      }
    else
      { print " actualizado."; }
    }

  print "\nHecho.";
  }

#---------------------------------

sub Libro
  {
  my $i, my $j;
  my $Origen = "";
  my $Destino = $DirectorioCurso . "libro/libro.pdf";

  print "\nCreando libro.pdf... ";

  for ( $i=0 ; $i &lt; scalar(@Libro) ; $i++ )
    {
    for ( $j=0 ; $j &lt; scalar(@{$Libro[$i]{'contenido'}}) ; $j++ )
      {
      my $Clave = $Libro[$i]{'contenido'}[$j]{'clave'};
      my $Blanca = $Libro[$i]{'contenido'}[$j]{'blanca'};
      $Origen .= $DirectorioCurso . "postscript/" . $Clave . ".ps ";
      if ( length ($Blanca) )
        { $Origen .= $DirectorioCurso . "postscript/" . $Blanca . ".ps "; }
      }
    }

  CreaPDF ($Origen, $Destino);
  print " hecho.";
  }

#---------------------------------

sub CopiaExt
  {
  print "\nCopiando a unidad externa $UnidadCopiaExt...";

  # Montamos la unidad
  my $Orden = "mount $UnidadCopiaExt";
  system ($Orden);

  # Hacemos la copia con rsync
  my $Orden = "cd $DirectorioCurso;";
  $Orden .= "rsync --delete -a resultado seguridad $DirectorioCopiaExt";
  system ($Orden);

  # Desmontamos la unidad
  my $Orden = "umount $UnidadCopiaExt";
  system ($Orden);

  print "\nHecho.";
  }

#---------------------------------

sub CopiaWeb
  {
  print "\nCopiando a servidor $DirectorioCopiaWeb...\n";

  # Hacemos la copia con rsync
  my $Orden = "cd $DirectorioCurso;";
  $Orden .= "rsync --delete -a -e ssh hojas resultado seguridad " .
            $DirectorioCopiaWeb;
  system ($Orden);

  print "\nHecho.";
  }

#---------------------------------

sub NecesitaActualizar
  {
  # Tomamos los parámetros
  my ($Origen, $Destino) = @_;

  # $Origen es una cadena con muchos nombres de archivo
  # separados por espacios; obtenemos un array con
  # todos los nombres de archivo
  my ( @ListaArchivos ) = split (/ /, $Origen);

  # Nos quedamos con el momento del archivo origen más reciente
  my $MomentoOrigen = 0;
  foreach my $Archivo (@ListaArchivos)
    {
    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
    $atime,$mtime,$ctime,$blksize,$blocks)
      = stat($Archivo);
    if ( $mtime &gt; $MomentoOrigen )
      { $MomentoOrigen = $mtime; }
    }

  # Vemos el momento del archivo $Destino
  my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
  $atime,$mtime,$ctime,$blksize,$blocks)
    = stat($Destino);
  my $MomentoDestino = $mtime;

  # Habrá que actualizar cuando el Origen sea más moderno
  # que el Destino
  return ( $MomentoOrigen &gt; $MomentoDestino );
  }

#---------------------------------

sub Analiza
  {
  # Creamos un analizador XML
  my $Analizador = new XML::Parser
     ( Handlers =&gt;
         {
         Start =&gt; \&amp;ElementoInicial,
         End   =&gt; \&amp;ElementoFinal,
         Char  =&gt; \&amp;ElementoDato
         }
     );

  # Lanzamos el análisis
  $Analizador-&gt;parsefile ($Descripcion);
  }

#---------------------------------

sub ElementoInicial
  {
  # Tomamos los parámetros
  my ($Manejador, $Nombre) = @_;

  if ( $Nombre eq "parte" )
    {
    $Profundidad = PARTE;
    $Punto = 0;
    @Contenido = ();
    }

  if ( $Nombre eq "hoja" )
    {
    $Profundidad = HOJA;
    $Punto = 0;
    $Hoja{'blanca'} = "";
    }

  if ( $Nombre eq "nombre" )
    { $Punto = NOMBRE; }

  if ( $Nombre eq "clave" )
    { $Punto = CLAVE; }

  if ( $Nombre eq "tipo" )
    { $Punto = TIPO; }

  if ( $Nombre eq "blanca" )
    { $Punto = BLANCA; }
  }

#---------------------------------

sub ElementoFinal
  {
  # Tomamos los parámetros
  my ($Manejador, $Nombre) = @_;

  if ( $Nombre eq "parte" )
    {
    $Profundidad = 0;
    $Parte{'contenido'} = [@Contenido];
    push (@Libro, {%Parte});
    }

  if ( $Nombre eq "hoja" )
    {
    $Profundidad = PARTE;
    push (@Contenido, {%Hoja});
    }
  }

#---------------------------------

sub ElementoDato
  {
  # Tomamos los parámetros
  my ($Manejador, $Dato) = @_;

  # Eliminamos los blancos al comienzo y al final
  $Dato =~ s/^\s+//g;
  $Dato =~ s/\s+$//g;

  # Si el dato recibido solo tiene blancos, no nos vale
  if ( length ($Dato) )
    {
    # Hay que pasar de Unicode a Latin1
    $Dato = Unicode::String::utf8($Dato)-&gt;latin1;

    # Anotamos el dato donde corresponda

    if ( $Profundidad == PARTE )
      {
      if ( $Punto == NOMBRE )
        { $Parte{'nombre'} = $Dato; }
      if ( $Punto == CLAVE )
        { $Parte{'clave'} = $Dato; }
      }

    if ( $Profundidad == HOJA )
      {
      if ( $Punto == NOMBRE )
        { $Hoja{'nombre'} = $Dato; }
      if ( $Punto == CLAVE )
        { $Hoja{'clave'} = $Dato; }
      if ( $Punto == TIPO )
        { $Hoja{'tipo'} = $Dato; }
      if ( $Punto == BLANCA )
        { $Hoja{'blanca'} = $Dato; }
      }
    }
  }
</pre></body></html>