Personalizar con A-PDF Text Replace

A-PDF Text Replace, muy bueno para retocar PDFs, también utilizable desde la línea de mandatos

He empezado a utilizar A-PDF Text Replace que es una herramienta muy útil para modificar documentos en formato PDF.

Por ejemplo, sirve para personalizar un mismo documento PDF para diferentes personas. Para ello se puede poner en el documento PDF original un texto que haga de campo a reemplazar, por ejemplo, _NOMBRE_, y luego utilizar A-PDF Text Replace para cambiar _NOMBRE_ por el nombre de cada persona. Como con A-PDF Text Replace se pueden dar varias órdenes de cambio en la misma ejecución, se puede personalizar simultáneamente el nombre, la fecha, la dirección u otros campos.

A-PDF Text Replace es también invocable desde la línea de mandatos, por lo que puede invocarse con System() desde el lenguaje TOL, como se muestra en el siguiente código:


///////////////////////////////////////////////////////////////////
Real PdfReplace(Text inpPth, // Input file path
                Text outPth, // Output file path
                Text namCus) // Customer name
///////////////////////////////////////////////////////////////////
{
  Text pdfCmd = W("bin/apdf/ptrcmd.exe"); // A-Pdf executable path
  Text inpDos = Q(W(inpPth));
  Text outDos = Q(W(outPth));

  Text forOld = Q(":e:");
  Text namOld = Q(":a:");
  Text dteOld = Q(":c:");

  Text forNew = Q("Edición personalizada para");
  Text namNew = Q(namCus);
  Text dteNew = Q("el "+Dte2Spa(Now)+"."); // Spanish date

  Text cmdTxt = pdfCmd+" "+inpDos+" "+outDos+" "+
                           forOld+" "+forNew+" "+
                           namOld+" "+namNew+" "+
                           dteOld+" "+dteNew;
                            
  Real sysRet = System(cmdTxt); // Execute

  sysRet // Returns
};
  

Donde Q() y W() son dos pequeñas funciones donde la primera pone comillas en los caminos de los ficheros, por si esos nombres contuvieran caracteres blancos, y la segunda cambia el slash normal ( / ) por el back slash de los caminos de Windows ( \ ).


///////////////////////////////////////////////////////////////////
Text Q(Text txt) // Text
///////////////////////////////////////////////////////////////////
{ Char(34)+txt+Char(34) };

///////////////////////////////////////////////////////////////////
Text W(Text txt) // Text
///////////////////////////////////////////////////////////////////
{ Replace(txt, "/", "\\") };
  

La siguiente ventana muestra la ejecución de A-PDF Text Replace invocado desde TOL. El mandato que se esta ejecutando en la ventana es:


bin\apdf\ptrcmd.exe
"..\Ediciones\9788493912901.Becquer.Gustavo. Adolfo.Leyendas\
BecquerGA.Leyendas.20110802.aContracorriente.PackDigital\BecquerGA.
Leyendas.20110801.aContracorriente.pdf"
"aContracorriente\BecquerGA.Leyendas.20110801.aContracorriente.pdf"
":e:" "Edición personalizada para"
":a:" "Antonio Salmeron"
":c:" "el 9 de septiembre de 2011."
  

Donde ":e:", ":a:" y ":c:" son los campos que se utilizan para ser remplazados por los texto de personalización.

A-PDF Text Replace, mandato invocado desde TOL

El resultado es el que se muestra en la siguiente imagen. A la izquierda el PDF original, a la derecha el PDF modificado.

A-PDF Text Replace, el PDF antes y después

Es la personalización de una edición digital de las Leyendas de Gustavo Adolfo Bécquer. Los campos seleccionados en el primer PDF corresponden a las marcas, los campos seleccionados en el segundo PDF a los valores finales de la personalización.

Buscar datos en Mozilla Backup de Thunderbird

Para buscar datos (direcciones, teléfonos, fechas de envío, etc.) que estaban en un email borrado, pero guardado en un backup realizado con Mozilla Backup (MozBackup) para el sistema de gestión de correo electrónico Thunderbird, no es necesario restaurar el backup. Lo que es especialmente relevante para backups de más de un Gbyte.

El fichero de MozBackup (extensión .pcv) es un fichero comprimido, que se puede abrir con 7-zip, también con WinZip y WinRar, y volcar todo su contenido a un directorio temporal. Este directorio sigue la estructura de los directorios de trabajo de Thunderbird.

Una vez volcado se puede navegar, manualmente, por el árbol de directorios para localizar la carpeta en la que se cree que esta el correo. Esta navegación también se podría hacer dentro del fichero comprimido, pero me resulta mucho más incómoda.

Si no se sabe muy bien donde pueden estar los datos o los correos electrónicos, se puede localizar el fichero o los ficheros que los contienen con alguna utilidad de búsqueda recursiva en un árbol de directorios y aquellos patrones de búsqueda que creamos más significativos. En estos casos, en Windows, utilizo grep.exe de GNU (GnuGrep) con la opción -r de recursivo, por ejemplo: \bin\gnuwin32\bin\grep -r -l -c "asolver.com" ImapMail para buscar correos del dominio asolver.com sólo en la carpeta ImapMail, lo más cómodo es buscarlo en el total (.).

Hay que tener en cuenta que el caracter arroba (@) típico de las direcciones de correos a veces aparece como tal en las cabeceras de los correos electrónicos y otras como =40 siendo el 40 el código ASCII hexadecimal de la arroba (64 decimal, el anterior a la A y el doble que el caracter blanco), como sucede en el cuerpo de los correos. Por lo que se podrían probar búsquedas como "asalmeron@asolver.com" o "asalmeron=40asolver.com".

Si finalmente hay que examinar visualmente el fichero o los ficheros localizados por grep.exe, como son ficheros planos, pueden abrirse con un editor de textos para buscar la información localmente. De todos los editores posibles utilizo para ello Notepad2.

Resolver ecuaciones de segundo grado

Resolver ecuaciones de segundo grado

Pequeño fichero Excel (Libro de Excel 97-2003) con una sola hoja de cálculo que permite resolver las ecuaciones de segundo grado y obtener sus dos soluciones.

En la parte izquierda de la hoja de cálculo, a partir de la ecuación sen encuentran sus 2 raices, se factoriza la ecuación de segundo grado como (x - 1ª raiz) . (x - 2ª raiz) y se vuelve a reconstruir la ecuación para comprobar que todo es correcto.

La parte derecha de la hoja de cálculo, permite generar problemas sencillos de ecuaciones de segundo grado para su posterior solución.

Las celdas azules son para introducir los datos y las celdas verdes para ver los resultados.

Comprobar divisiones con resto

Para comprobar el cociente y el resto de divisiones

Pequeño fichero Excel (Libro de Excel 97-2003) con una sola hoja de cálculo que permite comprobar el cociente y el resto de divisiones.

También permite comprobar divisiones cuando se pide calcular el cociente con 2 decimales.

Las celdas azules son para introducir los datos y las celdas verdes para ver los resultados.

Dígitos de control de una cuenta bancaria

Cálculo de dígitos de control de una cuenta bancaria

Hoja de cálculo Excel (Libro de Excel 97-2003) para calcular los dos de dígitos de control de una cuenta bancaria.

El cálculo de los dígitos de control se realiza a partir de los códigos de banco, sucursal y cuenta, introduciéndolos en las celdas Excel dígito a dígito.

Esta hoja Excel permite conocer el mecanismo de cálculo de ambos dígitos de control, mediante:

  • los textos explicativos que contiene la hoja,
  • su diseño de cálculo por fases (datos de entrada, parámetros de cálculo, resultados intermedios y resultados finales) y
  • las fórmulas visibles dentro de sus celdas.

Ecuaciones de 2 a 6 incógnitas con matrices

Resolver ecuaciones de 2 a 6 incógnitas con matrices

Fichero Excel (Libro de Excel 97-2003) con 6 hojas de cálculo para resolver pequeños sistemas de ecuaciones de 2 a 6 incógnitas.

En las 2 primeras hojas de cálculo se resuelven sistemas de 2 ecuaciones de con 2 incógnitas. En la primera mediante la función Excel inversa de una matriz y en la segunda mediante el cálculo paso a paso de la inversa de una matriz 2x2.

En las 4 siguientes hojas de cálculo se resuelven sistemas de ecuaciones con 3, 4, 5 y 6 incógnitas, mediante la función Excel inversa de una matriz (MINVERSA()).

Las 2 últimas hojas de cálculo (con 5 y 6 ecuaciones) también contienen un área de celdas para la comprobación de los resultados.

Estas hojas de cálculo Excel permiten conocer el mecanismo de cálculo empleado, mediante:

  • su diseño de cálculo por fases, especialmente en la 2ª hoja, con datos de entrada, resultados intermedios y resultados finales y
  • las fórmulas visibles dentro de sus celdas.

La función Excel MINVERSA() es una fórmula matricial que afecta a varias celdas de forma simultánea. La introducción de este tipo de fórmulas matriciales es diferente al de las funciones normales de Excel. Los pasos para introducir una fórmula matricial son:

  • Se selecciona el rango de celdas que ocupará la matriz destino, por ejemplo, un rango 2x2, empezando por la celda de la esquina superior izquierda.
  • Se pulsa la tecla F2 que activa la posibilidad de introducir una fórmula en la celda de la esquina superior izquierda (que es por la que se comenzó la selección). Entonces se teclea la fórmula matricial, por ejemplo: =MINVERSA(T6:U7).
  • Una vez tecleada la fórmula y con el rango de celdas destino que sigue seleccionado se pulsan simultáneamente las teclas Control+Shift+Intro. Entonces la fórmula matricial se expande a todo el área seleccionada y Excel la visualiza entre corchetes, como {=MINVERSA(T6:U7)}, para señalar que es una fórmula matricial.
  • Nota: Una vez tecleada la fórmula no hay que pulsar Intro, ni el botón llamado introducir (v) que está a la izquierda del área de introducción de fórmulas.

5 palabras terminadas en u de género femenino

Me vinieron con la pregunta del colegio, ¿hay palabras terminadas en u de género femenido?, pues las hay, aunque para encontrar las 5 siguientes tardé un tiempo. De las 5 la que más me gusta es tribu:

  • cu: Femenino. Nombre de la letra q.
  • u: Femenino. Vigésima cuarta letra del abecedario español.
  • interviú: Del inglés interview. Ambiguo, pero usado más en femenino. Entrevista, acción y efecto de entrevistar.
  • mu: Femenino. En desuso. Sueño, acto de dormir. Era usado por las nodrizas cuando querían que se durmieran los niños, diciéndoles: Vamos a la mu.
  • tribu: Del latín tribus. Femenino. Cada una de las agrupaciones en que algunos pueblos antiguos estaban divididos o grupo social primitivo de un mismo origen.

Las siguientes 16 líneas de código TOL permiten localizar de un fichero de palabras, una por línea, las terminadas en u y volcarlas en otro fichero, igualmente una por línea, encontrar las de género femenino, sin más información, es ya más personal:


Text inpFil = "ficherodepalabras.txt";
Text outFil = "u.txt";
Text WriteFile(outFil, "");

Text filTxt = ReadFile(inpFil);
Set  linSet = Tokenizer(filTxt, "\n");
Set  linCic = EvalSet(linSet, Real(Text linTxt)
{
  Real len = TextLength(linTxt);
  Text end = Sub(linTxt, len, len);
  If(end <: [["u", "ú"]],
  {
    Text WriteLn(linTxt);
    Text AppendFile(outFil, linTxt+"\n");
    1
  },0)
});
  

Solución a un 3D Squares

Resolver y solucion de un 3D Squares de insectos

Hay puzzles que les regalan a los niños que parecen triviales y no lo son, a veces ni para los niños ni para los adultos. Para mi este es un caso claro.

Para resolverlo tuve que programar un solucionador en TOL que no resulto muy complicado. De hecho mucho menos complicado que resolver manualmente el puzzle 3D Squares. Aunque el código completo junto con su traza de ejecución puede obtenerse en PDF al final de este post, pongo a continuación en algoritmo recursivo fundamental. Este algoritmo encuentra 4 soluciones iguales, pues unas resultan de un giro de 90º de otras:


///////////////////////////////////////////////////////////////////
Real SolCua(Set entSol, Set entPie) // Resuelve
///////////////////////////////////////////////////////////////////
{
  Real crdSol = Card(entSol);
  Text Write(FormatReal(crdSol,"%.0lf"));

  If(EQ(crdSol, 9), SolVer(entSol), // Ha encontrado una solucion
  {                 // Busca soluciones
    EvalWhile(entPie, Real(Text unoPie)
    {
      Set salPie = entPie - [[unoPie]];
      Set varPie = VarAll(unoPie);
      EvalWhile(varPie, Real(Text unoVar)
      {
        Set salSol = entSol << [[ unoVar ]];
        If(SolBue(salSol), SolCua(salSol, salPie), FALSE)
      })
    })
  })
};
  

Puertas lógicas AND, OR, XOR y Sumador en Excel

Puertas lógicas AND, OR, XOR y Sumador con acarreo programados en Excel

Ejemplo de programación en Excel de puertas lógicas AND, OR, XOR y Sumadores con acarreo (la versión es la de Excel 97-2003).

En la primera hoja se ven las puertas lógicas AND (Y), OR (O) y XOR (O exclusivo) y sus tablas de verdad.

En la segunda hoja un sumador con acarreo y su tabla de verdad construido mediante las puertas anteriores.

La tercera hoja contiene 3 versiones de sumadores con acarreo similares al de la hoja anterior.

En la cuarta hoja se implementa un sumador de 3 bits a partir de los sumadores anteriores.

Incluye todas las pistas de conexión entre las puertas, con diferentes colores, para ver la transmisión de la información.

Tablas de multiplicar y multiplicaciones

Tablas de multiplicar y multiplicaciones de 1 cifras por varias

Fichero Excel de Microsoft con tablas de multiplicar y multiplicaciones de 1, 2, 3, 4 y 5 dígitos por 1 dígito. Plantea multiplicaciones, hay que teclear los resultado e informa de los errores de tal manera que si el número sale azul es que la multiplicación está bien, si sale en rojo es que está mal.

En cada una de las 6 hojas Excel hay una celda negra que permite generar 9 conjuntos diferentes de combinaciones de operaciones (del 1 al 9).

Las soluciones también pueden consultarse en la parte inferior de cada hoja Excel en el área coloreada de gris. La versión es la de Excel 97-2003.

GNU Plot, TOL, Html2Pdf y series temporales

Informes gráficos en PDF con GNU Plot + TOL + Two Pilots Html2Pdf

En la documentación adjunta a este post se cuenta el proceso de investigación sobre cómo automatizar la generación de informes con gráficos de series temporales mediante la combinación de GNU Plot con TOL con Html2Pdf de Two Pilots. El objetivo es mecanizar informes gráficos sin intervención humana.

Se presentan cuatro ejemplos, crecientes en complejidad:

  • El primero genera un gráfico simple en formato GIF utilizando GNU Plot, se trata de un programa de demostración de GNU Plot ligeramente modificado.
  • El segundo muestra la generación de un gráfico en formato GIF de una serie temporal larga con datos que proceden del Banco de Canada (Bank of Canada).
  • El tercero muestra cómo generar un gráfico en formato GIF con una serie temporal almacenada en el formato BDT de TOL. También se construye un fichero HTML que contiene el gráfico y lo convierte a PDF llamando tanto a GNU Plot como al conversor a PDF (Two Pilots Html2Pdf) desde TOL.
  • En el cuarto se genera el mismo gráfico que en el tercero pero en 3 formatos diferentes JPEG, GIF y PNG, se insertan los tres gráficos dentro de una página HTML y se produce el PDF utilizando la mejor calidad posible para la generación de los JPEG internos del PDF. Como se explica en el fichero PDF adjunto, al final de este post, el formato que proporciona mejores resultados es el PNG (formato que desde hace tiempo me gusta mucho, por ejemplo, todas las imágenes de este web están en formato PNG).
  • Finalmente en el quinto se utiliza multiplot para generar un gráfico del estilo de los que se emplean en los mercados bursátiles con datos de la operadora de telecomunicaciones Jazztel. También en este ejemplo, dejo de utilizar la extensión de demostración de los ficheros de GNU Plot (.dem) para utilizar la genuina (.plt).

Adjunto a continuación un trozo del código (el código fuente completo se puede ver en el documento adjunto a este post) donde se generan las especificaciones para GNU Plot a partir de una plantilla e igualmente para la página HTML y se invoca a GNU Plot y a Two Pilots Html2Pdf. La plantilla para GNU/Plot está inserta en el propio código TOL y la plantilla para la página HTML se lee de un fichero externo:


///////////////////////////////////////////////////////////////////
// GnuPlot prototype
///////////////////////////////////////////////////////////////////
Text ploSed =
"
set term gif font 'arial' 10 size SIZWID, SEDHEI
set output 'FILGIF'
set datafile separator ';'
set title 'GRATIT' textcolor lt 3 font 'arial,14'
set style data lines
set timefmt '%d/%m/%Y'
set xdata time
set xlabel 'LABELX' textcolor lt 3 font 'arial,12'
set xrange [ 'DTEINI':'DTEEND' ]
set xtics rotate by 90
set format x '%d/%m/%Y'
set ylabel 'LABELY' textcolor lt 3 font 'arial,12'
set yrange [ 0 : ]
set format y '%g'
set grid
set key left
plot 'FILDAT' using 1:2 t 'SERNAM'
reset
";


///////////////////////////////////////////////////////////////////
// Dates and names
///////////////////////////////////////////////////////////////////
Text iniTxt = FormatDate(First(UniqueUsers), "%d/%m/%Y");
Text endTxt = FormatDate(Last (UniqueUsers), "%d/%m/%Y");
Text serNam = Name(UniqueUsers);


///////////////////////////////////////////////////////////////////
// Build and write the GnuPlot command file
///////////////////////////////////////////////////////////////////
Text ploDem = ReplaceTable(ploSed,
  [[ [["SIZWID",  "800"]], [["SEDHEI",  "500"]],
     [["FILGIF", filGif]],
     [["GRATIT", "Demo: BDT to GIF Chart"]],
     [["LABELX", "Weeks"]],
     [["DTEINI",  iniTxt]], [["DTEEND", endTxt]],
     [["LABELY", serNam]],
     [["FILDAT", filDat]],
     [["SERNAM", serNam]] ]], 1);
Text WriteFile(filDem, ploDem);


///////////////////////////////////////////////////////////////////
// Run GnuPlot
///////////////////////////////////////////////////////////////////
Text cmdExe = "C:\\asc\\Bin\\gnuplot\\bin\\pgnuplot";
Text cmdTxt = cmdExe+" "+filDem;
Real cmdRun = System(cmdTxt); // Execute gnuplot


///////////////////////////////////////////////////////////////////
// Makes a Pdf file from the Html page
///////////////////////////////////////////////////////////////////
Text codHtm = ReplaceTable(ReadFile(filSed),
  [[ [["SIZWID",  "800"]], [["SEDHEI",  "500"]],
     [["FILGIF", filGif]],
     [["PLOCOD", ploDem]] ]], 1);
Text WriteFile(filHtm, codHtm);


///////////////////////////////////////////////////////////////////
// Makes a Html page from a seed
///////////////////////////////////////////////////////////////////
Text pdfExe = "\""+"C:\\Archivos de programa\\Two Pilots\\"+
                      "HTML2PDF Pilot\\HTML2PDF_Pilot.exe"+"\"";
Text pdfInp = "C:\\asc\\App\\GnuPlot.Test\\"+filHtm;
Text pdfOut = "C:\\asc\\App\\GnuPlot.Test\\"+filPdf;
Text pdfCmd = pdfExe+" "+             // Executable pdf convertor
              pdfInp+" "+             // Input file
              pdfOut+" "+             // Output file
              " /jpeg 100 /author asalmeron@asolver.com";

Real pdfRun = System(pdfCmd); // Execute html to pdf conversion
    

La documentación adjunta incluye, en el mismo orden anterior (salvo los tres códigos fuente HTML que están al final del todo): el código completo de especificaciones para GNU Plot, el código fuente TOL, los datos de series temporales utilizados en los gráficos y los ficheros de mandatos.

Ajax para el cambio de divisas

Conexión Ajax con el Rss del Banco de Canada para el cambio de divisas

¿Aburrido de tener que ir divisa a divisa buscando el cambio cada día, para copiarlo e introducirlo en hojas Excel?. Así estaba yo hasta que me programé esta combinación de JavaScript y PHP para leer los RSS (Really Simple Syndication en formato XML) del Banco de Canada (Bank of Canada), de siempre mi banco central favorito, para obtener una tabla de cambios con mis divisas habituales y poder pegar con comodidad esa tabla en las hojas Excel que necesitaban esos cambios actualizados.

El resultado es una página web, con un estilo vintage muy a mi gusto, en colores de computador clásico y con la preciosa Lucida Console por tipo de letra.

Todo el código JavaScript y PHP (PHP Hypertext Pre-processor ) puede consultarse en PDF (Portable Document Format) al final de este post, sólo pongo a continuación un par de funciones, la primera JavaScript y la segunda PHP, que muestran como se puede tener un estilo de programación similar con leguajes de programación diferentes.


///////////////////////////////////////////////////////////////////
   function XmlGetValue(itm, atr)

// PURPOSE: Returns a text value. If atr = cb:value and itm =
// ...<cb:value frequency="business" decimals="4">0.9947</cb:value>...
// first gets " frequency="business" decimals="4">0.9947<"
// and returns 0.9947
///////////////////////////////////////////////////////////////////
{
  var tmp = TxtBetween2Tag(itm,"<"+atr,"/"+atr+">");
  var txt = TxtBetween2Tag(tmp,">","<");
  return(txt);
}

///////////////////////////////////////////////////////////////////
   function GetRssFile()
   
// PURPOSE: Gets the contents of an external Rss feed and saves its
// contents to the cached file on the server.
///////////////////////////////////////////////////////////////////
{
  global $rssUrl, $pthFile;
  $contents=file_get_contents($rssUrl); // Fetch the Rss file
  $fp=fopen($pthFile, "w");             // Open local file for write
  fwrite($fp, $contents);     // Write contents of Rss to cache file
  fclose($fp);                          // Close the local file
}
  

Contador de separadores de campos

En una gran aseduradora nos volcaban ficheros inmensos de datos de operaciones sobre polizas desde el host, ficheros con millones de registros. Eran ficheros planos y ellos mismos elegían el carácter separador de campos. Esos ficheros los subíamos, a su vez, a otra base de datos que los cargaba utilizando, para interpretar la separación de campos, el carácter indicado en cada caso. Esta carga se realizaba empleando el programa de carga suministrado junto con el sistema de gestión de la base de datos.

El proceso de carga era lento y, además, con frecuencia, el carácter que habían elegido, aparecía en algún campo de texto de algún registro. Por ejemplo, elegían el asterisco y algunos campos de observaciones contenían asteriscos. Al llegar al primer registro con más separadores de los esperados el proceso de carga abortaba tras horas de ejecución.

La solución era programar un comprobador de ficheros, muy rápido, que antes de iniciar el proceso de carga, comprobara que todas las líneas del fichero contuvieran el mismo números de caracteres separadores de campo, asumiendo como correcto el número de separadores de la primera línea del fichero. Y sólo comenzar la carga a la base de datos tras comprobar que el fichero era correcto.

Hacía ya mucho tiempo que no programaba en C y esta era una ocasión para volver a él. El programa se llama chksep.exe, esta hecho en lenguaje C en pocas horas, por lo que seguro que se puede mejorar. Se ejecuta como chksep 42 < fichero.txt, donde el 42 es el código ASCII del caracter *, que era el separador de campos habitual, aunque se puede elegir cualquier otro carácter, por ejemplo, chksep 59 < fichero.txt, para el punto y coma.

El programa visualiza las líneas donde el número de separadores de campo es diferente al de la primera línea. En un computador portatil normal este programa es capaz de revisar un Giga en 2 minutos 15 segundos, lo que equivalía con los ficheros reales que nos daban a unos 800.000 registros por minuto. Por lo que en 5 ó 10 minutos se podía evitar que luego abortara una carga de horas. El programa también cuenta el número de líneas del fichero.

A continuación pongo el ciclo principal del programa y en el fichero PDF adjunto a este post el código fuente completo.

Me gustan mucho los programas eficaces de pocas líneas de código y que sean eficientes ya lo considero un regalo.


while((chrInp=getc(stdin)) != EOF)
{
       if(chrInp==sepChr) { sepCnt++; }     /* Es un separador */
  else if(chrInp==EOL)                 /* Es un salto de linea */
  {
    if(linCnt==1)                  /* Estabamos en la 1ª linea */
    {
      sepFst = sepCnt;   /* Referencia para el resto de lineas */
      printf("\n[%d] [%s] en la primera linea.", sepFst, sepStr);
    }
    else                                       /* Linea normal */
    {
      if(sepFst!=sepCnt)        /* Faltan o sobran separadores */
      {
        printf("\nlinea %d: [%d] [%s] en vez de [%d].",
               linCnt, sepCnt, sepStr, sepFst);
      }
    }
    linCnt++;             /* Incrementar el contador de lineas */
    sepCnt = 0;     /* Poner a cero el contador de separadores */
  }
}
printf("\nProcesadas [%d] lineas.", linCnt);
  

Máximo común divisor de 2 números

Cálculo en Excel del máximo común divisor (MCD) de 2 números

Ejemplo de hoja Excel para calcular el máximo común divisor (MCD) entre 2 números.

El fichero Excel de Microsoft está formado por una sola hoja de cálculo en la que los 2 números para los que se quiere calcular su máximo común divisor se introducen en las celdas azules.

La hoja Excel visualiza en la zona gris los divisores encontrados y arriba en grande y azul el máximo común divisor.

Este ejemplo Excel tiene limitaciones en el tamaño de los números y el método de cálculo.

La versión es la de Excel 97-2003.

Poemas jué jù, dinastía Táng, en Excel

Poemas jué jù, dinastía Táng, en Excel

En 2008 comencé a plasmar en Excel el resultado de mis traducciones de poemas de la disnastía Táng que luego imprimía en formato PDF. Realmente, no sólo a plasmar, pues al final llegué a traducir directamente sobre Excel.

Desarrollé varios modelos de hojas Excel, de los cuales uno de los últimos va adjunto a este post. Cuando alguien de una multinacional o gran compañía consultora se empeñaba en enseñarme y presumir de sus fabulosas hojas financieras Excel entonces, en vez de mostrarle otras hojas financieras, yo terminaba por eseñarle estas hojas Excel de soporte a la traducción de poemas mediavales chinos y el show de hojas Excel se terminaba rápido.

Excel daba más soporte que mis experientos iniciales en PowerPoint, pues permitía automatizar muchas tareas, pero llegó un punto, como suele suceder con Excel, que la complejidad de las hojas y las fórmulas, y especialmente su repetición en todas las hojas, dificultaban el avance, e incluso el recálculo de celdas podía hacerse muy pesado y la impresión de las hojas de resultados finales, no de intermedios, requería mucha atención.

Excel es, en mi opinión, lo mejor que ha hecho Microsoft en su historia. Muy pocas herramientas proporcionan un desarrollo inicial tan rápido como Excel, pero la velocidad de desarrollo de Excel se amortigua con el avance, hoy se avanza menos que ayer y más que mañana, por lo que para desarrollo de pocos días es ideal y para desarrollos de muchos meses insoportable.

Por ello, la vida de estas hojas Excel fue de mediados de 2008 a mediados 2009, prácticamente un año, a partir del cual retome la exploración de nuevas formas de dar soporte a la traducciones de poemas jué jù Táng buscando mayor automatismo y eficiencia en la traducción y composicion del resultado.

Varias publicaciones de mis traducciones de poemas Táng fueron realizadas con el soporte de Excel.

Tabla ASCII generada con TOL

Tabla ASCII generada con el lenguaje de programación TOL

Este es un programa sencillo, orientado al aprendizaje del lenguaje TOL.

El programa escribe un fichero con los 128 primeros caracteres de la tabla ASCII.

Los 32 primeros son caracteres especiales por lo que sus siglas se almacenan en un conjunto TOL (Set).

Los siguientes caracteres se generan mediante la función TOL Text Char(Real asc).

Se muestra a continuación el código completo del programa que también se puede consultar en el documento PDF adjunto a este post, se incluye junto con el código el fichero resultante con la tabla ASCII.


Set AscFst =
[[
  "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
  "BS ", "TAB", "LF ", "VT ", "FF ", "CR ", "SO ", "SI ",
  "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", 
  "CAN", "EM ", "SUB", "ESC", "FS ", "GS ", "RS ", "US "
]];

Text fil = "ascii.txt";
Text WriteFile(fil, "Tabla ASCII");

Set  cic = For(0, 127, Text(Real asc)
{
  // Each 8 jump new line
  Text new = If(asc % 4, "", "\n");

  // The fist 32 are special characters
  Text chr = If(LT(asc, 32), AscFst[asc+1], "["+Char(asc)+"]");

  // Append character to file
  Text lin = new + FormatReal(asc, "%03.0lf") + " " + chr +" | ";
  Text AppendFile(fil, lin);
  
  chr // Return the character
});
  

15 puzle (3x3) y 65 puzle (5x5)

Excel con el 15 puzle (3x3) y 65 puzle (5x5) resueltos

Fichero Excel de Microsoft formado por una sola hoja de cálculo, que contiene:

  • El llamado problema del 15 puzle, de 3x3 celdas, con todos los números de 1 al 9, uno por celda sin repetirse, que suman siempre 15 tanto por filas, como por columnas y diagonales, y
  • El problema del 65 puzle, menos conocido que el anterior, de 5x5 celdas, con todos los números de 1 al 25, uno por celda sin repetirse, que suman siempre 65 tanto por filas, como por columnas y diagonales.

En este fichero Excel se muestran ambos problemas ya resueltos. La versión es la de Excel 97-2003.

Triángulo mágico de la diosa hindú Durga

Excel para resolver el problema del triángulo mágico de la diosa Durga

Fichero Excel (Libro de Excel 97-2003) con 4 hojas de cálculo. En la primera hoja se plantea resolver el problema del llamado triángulo mágico de la diosa hindú Durga.

Hay que poner los números del 1 al 9, todos y sin repetición, en los 3 lados del triángulo, de forma que los 3 lados sumen lo mismo. En cada lado se ponen 4 números del 1 al 9 y hay 3 celdas que son comunes a 2 lados, teniendo en total 9 celdas.

Las 3 hojas siguiente muestran las 3 soluciones posibles. En principio no habría que verlas hasta haber solucionado por uno mismo el problema del triángulo.

Las operaciones se corrigen automáticamente. Si el número introducido sale azul es que está bien y si sale en rojo es que está mal.

En la primera hoja hay una celda de control que permite fijar la solución que hay que buscar de entre las 3 existentes (sin contar giros).

640 palabras con las 5 vocales, una de cada

Creo que hay 640 palabras en español que contienen todas las 5 vocales a, e, i, o y u, una de cada y sin repetirse ninguna. Algunas de ellas son estupendas, por ejemplo, la primera de ellas abrenuncio, que es tan buena que la tengo apadrinada en la Reserva de Palabras.

A continuación enumero estas 640 palabras:

abrenuncio, aceituno, acudidero, acuífero, adoquier, adulterino, adulterio, aguerrido, aguiero, aguijeño, aguileño, ahuizote, ajicuervo, ajipuerro, albugíneo, almizqueño, amiguero, anfineuro, anguilero, angurriento, anquiseco, antequino, antetítulo, anticuerpo, aperturismo, aquenio, aquileño, ariqueño, arquetipo, arquíptero, arquitecto, arquitector, arseniuro, arundíneo, asecución, aucténtico, audímetro, aurífero, aurígero, auténtico, autocine, aztequismo, azufeifo, barbiluengo, barbiquejo, barquillero, betuminosa, bielorrusa, birrectángulo, bisabuelo, bisagüelo, blanquecino, blanquimento, blanquinegro, bolsiquear, boquifresca, boquillera, boquinegra, boquiseca, borinqueña, borriqueña, borriquera, botijuela, branquífero, braquícero, bribonzuela, brumamiento, bucelario, bufonería, buhonería, buñolería, burielado, buscamiento, buscapleitos, butadieno, butifarrero, buzamiento, cabeciduro, cachicuerno, calumbriento, campichuelo, cañihueco, capitulero, caquéctico, carguerío, carguillero, castilluelo, catequismo, caulífero, cauliforme, cauterio, cedulario, celulario, celulósica, censuario, centrifugado, centrifugador, centunvirato, cigoñuela, cigüeñato, cincuentavo, cincuentona, cochiquera, coguilera, colecturía, coliquera, collipullensa, comiquear, comisquear, comunicable, comunicante, concienzuda, concurrencia, conducencia, confesuría, confiturera, confluencia, confulgencia, conglutinante, congruencia, conquistable, consecutiva, contertulia, contradique, contumelia, contundencia, conventícula, coquería, coquinera, coquizable, corpulencia, correduría, cortisquear, cosquillear, criaduelo, cruentación, cruzamiento, cuadernillo, cuadriforme, cuadrillero, cuajicote, cuajilote, cuajiote, cuakerismo, cuarcífero, cuartelillo, cuartillero, cuatrerismo, cuellicorta, cuellilargo, cuentahílos, cuestación, cuestionar, cuicacoche, culteranismo, cuñaderío, cuodlibetal, curamiento, curanderismo, curialesco, curiosear, charquecillo, chupaderito, chuzonería, decuriato, deglutoria, degustación, delicaducho, delictuosa, delusoria, demudación, denticulado, denudación, denunciador, depuración, depurativo, desahucio, desboquillar, descontinua, descontinuar, descubridora, descuidado, descuitado, desdibujado, desguisado, deslánguido, despumación, destitulado, destruidora, desucación, desudación, deturpación, devolutiva, dominguera, doquiera, droguería, dudamiento, duenario, dulzainero, duodécima, duodecimal, duomesina, eburnación, ecuación, educación, educativo, elocutiva, emboquillar, embrosquilar, embustidora, embutidora, emulación, emulsionar, emundación, encáustico, encubridora, englutativo, enguichado, engullidora, engurriado, enjundiosa, enluciado, enlucidora, enmochiguar, enquillotrar, enquistado, enrubiador, ensuciador, entubación, enturbiador, entusiasmo, enunciado, equitador, equívoca, equivocar, eructación, eruginosa, erupcionar, erutación, escorbútica, escrutiñador, escuálido, escudillador, escudriñador, escultórica, escupidora, escupitajo, esfumación, esguízaro, espiráculo, esquiador, esquifazón, esquilador, esquinado, esquinanto, esquinazo, esquinzador, esquipazón, esquistosa, esquizado, estanquillo, estimulador, estimulosa, estuación, estuario, estudiado, estudiador, estudiantón, estudiosa, estuosidad, euboica, eubolia, eucalipto, eucrático, eufonía, eufónica, euforia, eufórica, eufótida, eutrofia, eutrófica, eutrofizar, evolutiva, excautivo, excluidora, exculpación, excusación, exhaustivo, exhumación, expugnación, expurgación, exudación, exultación, fabriquero, faleucio, fecundación, fecundativo, fecundizador, ferruginosa, feudalismo, filautero, flamenquismo, freudiano, frumentario, funerario, galleguismo, gatuperio, gerundiano, gesticulador, gesticulosa, granujiento, guadijeño, guaridero, guarnecido, gubernación, gubernativo, guijarreño, guineano, guionaje, guisandero, guitarreo, guitarrero, guitarresco, guitonear, gusaniento, hevicultora, hidalguejo, hidalgüelo, hieródula, hipotenusa, hociquear, hormiguear, hormigüela, hormiguera, hormiguesca, humectación, humectativo, humilladero, hurgamiento, hurtadineros, imbabureño, impetuosa, incestuosa, incompuesta, inconmutable, ineducado, inexhausto, infernáculo, inocultable, insepultado, interruptora, interurbano, invernáculo, iroquesa, irresoluta, jaquimero, jerárquico, juntamiento, juramiento, juzgamiento, latigueo, latiguero, latréutico, laudemio, laurífero, lauríneo, lectuario, leguario, leguminosa, lengüicorta, lengüilargo, letuario, lombriguera, longuería, loquería, loriguera, lucianesco, luciérnago, lucharniego, lustramiento, luteranismo, lloriquear, maldispuesto, mallequino, manguillero, manguitero, manigüero, manipuleo, maniqueo, manuelino, manutención, manutergio, maquilero, marisqueo, marisquero, matihuelo, meditabundo, menorquina, menstruación, mensuración, mensurativo, metalúrgico, meticulosa, milonguera, moquitear, morceguila, mordisquear, morisqueta, mosquitera, mudamiento, muestrario, multilátero, murciégalo, murciélago, murcigallero, narigueto, navichuelo, nebulizador, nebulosidad, neptuniano, neumático, neumonía, neumónica, neurálgico, neuroglia, neurótica, neutralismo, neutrónica, niquelado, niquelador, nucleario, numeración, numerario, numerosidad, obsequiar, obsequias, ocurrencia, ojienjuta, ojituerta, olisquear, opulencia, orquestina, orquídea, pacienzudo, palitoque, palitroque, pandemónium, pañizuelo, paquidermo, parquímetro, patimuleño, patituerto, patizuelo, pauperismo, paupérrimo, pecuario, peliagudo, perdulario, perduración, perfunctoria, perjudicado, perjudicador, permutación, persuadidor, persuasión, persuasivo, perturbación, peruanismo, peruviano, petrarquismo, picapuerco, pichagüero, pichuleador, pindonguear, piragüero, piruétano, pitanguero, plumbagíneo, porquecilla, porquería, porqueriza, precaución, preciosura, pregustación, prepositura, presunciosa, primuláceo, progenitura, pronunciable, prosénquima, pudelación, pujamiento, pulsamiento, pulverizador, punicáceo, punzonería, purgamiento, putrefacción, putrefactivo, quebradillo, quebradizo, quejicosa, quejillosa, querindango, quietador, quijotesa, quijotesca, quincallero, quinceavo, quinolear, quintaleño, quintalero, quiroteca, ranquelino, raquídeo, reasunción, rebollidura, recitáculo, reconquista, reconquistar, reconstructiva, recusación, redargución, reducidora, refugiado, refundidora, refutación, regulación, regulativo, reproductiva, republicano, reputación, requintador, resolutiva, resucitado, resucitador, resudación, reumático, reumatismo, revulsoria, riachuelo, rompesquinas, rosquillera, rostrituerta, rubefacción, rubiáceo, rufianesco, rumeliota, saduceísmo, sahumerio, salubérrimo, salutífero, sanguífero, sanguíneo, sanluiseño, sanluisero, saquerío, sardónique, secundario, secutoria, seguidora, segundario, sensualismo, sericultora, sesquiplano, seudónima, simultáneo, sobrequilla, soguería, sonrisueña, sortijuela, subarriendo, subdirectora, subdominante, subitáneo, sublevación, subordinante, subvencionar, sucesoria, sudorienta, sudorífera, sugeridora, sugestionar, superación, superádito, superiora, superlación, superlativo, supersónica, supervisora, supletoria, surrealismo, suspensoria, sustentación, tabiquero, tampiqueño, taquillero, taquímetro, taquinero, tenutario, tertuliano, teutónica, tiracuello, tiracuero, topiquera, toquería, tosiguera, triaquero, trirrectángulo, truncamiento, tuberosidad, tumefacción, turbamiento, turronería, ugrofinesa, ulceración, ulcerativo, ultraligero, unipersonal, univocarse, untamiento, urogenital, urticáceo, useñoría, vaqueiro, vaquerizo, ventrílocua, venusiano, vesiculosa, vestuario, vituperador, vituperosa, volumetría, volumétrica, vomipurgante, vulneración, vulnerario, yeguarizo, zatiquero y zurrapiento.

Las siguientes 35 líneas de código TOL muestran como extraer a partir de un fichero de palabras, una por línea, todas las palabras que tienen las 5 vocales, volcando en un fichero las que tienen las 5 vocales una de cada (sin repetición) y volcando en otro fichero las palabras que también tienen las 5 vocales pero con alguna repetida.


Text inpFil = "fichero.de.palabras.txt";
Text sinFil = "aeiou.sin.rep.txt";
Text conFil = "aeiou.con.rep.txt";

Text WriteFile(sinFil, "");
Text WriteFile(conFil, "");

Text filTxt = ReadFile(inpFil);
Set  linSet = Unique(Tokenizer(filTxt, "\n"));
Set  linCic = EvalSet(linSet, Real(Text linTxt)
{
  Text linRep = ReplaceTable(linTxt,
    [[ [["á", "a"]], [["é", "e"]], [["í", "i"]], [["ó", "o"]],
       [["ú", "u"]], [["ü", "u"]] ]]);

  Real aCnt = TextOccurrences(linRep, "a");
  Real eCnt = TextOccurrences(linRep, "e");
  Real iCnt = TextOccurrences(linRep, "i");
  Real oCnt = TextOccurrences(linRep, "o");
  Real uCnt = TextOccurrences(linRep, "u");
  
  If(And(EQ(aCnt,1), EQ(eCnt,1), EQ(iCnt,1), EQ(oCnt,1), EQ(uCnt,1)),
  {
    Text WriteLn(linTxt);
    Text AppendFile(sinFil, linTxt+"\n");
    1
  },
  If(And(GE(aCnt,1), GE(eCnt,1), GE(iCnt,1), GE(oCnt,1), GE(uCnt,1),
         GE(aCnt+eCnt+iCnt+oCnt+uCnt, 6)),
  {
    Text WriteLn(linTxt);
    Text AppendFile(conFil, linTxt+"\n");
    1
  },
  0))
});
  

Mini sistema de flujo de datos

Mini sistema de flujo de datos

Fichero con dos hojas Excel (Libro de Excel 97-2003) para practicar suma y resta con un mini sistema de flujo de datos. Las dos hojas Excel contienen el mismo flujo de datos, sólo difiere su anchura.

Las celdas de fondo verde claro son las que contienen los datos de partida. En las celdas de fondo azul claro es en las que hay que introducir los resultados. Las operaciones se corrigen automáticamente. Si el número introducido sale azul es que está bien y si sale en rojo es que está mal.

En la hoja hay dos celdas negras de control, arriba a la izquierda, que permiten fijar la complejidad de las operaciones (máximo) y generar diferentes conjuntos de operaciones.

Operaciones básicas + - x /, disposición horizontal

Practicar las 4 operaciones básicas, disposición horizontal

Fichero Excel (Libro de Excel 97-2003) con 4 hojas de cálculo para practicar sumas, restas, multiplicaciones y divisiones básicas.

Las operaciones se corrigen automáticamente. Si el número introducido sale azul es que está bien y si sale en rojo es que está mal.

En cada hoja hay dos celdas negras de control que permiten fijar la complejidad de las operaciones (máximo) y generar diferentes conjuntos de operaciones (actuando de semilla para la generación aleatoria de operaciones).

Operaciones básicas + - x /, disposición vertical

Practicar las 4 operaciones básicas, disposición vertical

Fichero Excel (Libro de Excel 97-2003) con 7 hojas de cálculo para practicar las operaciones básicas en disposición vertical:

  1. Hoja de sumas básicas.
  2. Hoja de restas básicas.
  3. Hoja para practicar las tablas de multiplicar.
  4. Dos hojas de multiplicaciones básicas.
  5. Dos hojas de divisiones básicas.

Las operaciones se corrigen automáticamente. Si el número introducido sale azul es que está bien y si sale en rojo es que está mal.

En cada hoja hay una celda negra de control que permiten generar nueve diferentes conjuntos de operaciones.

Imágenes como tablas en HTML

Poner en HTML una imagen como una tabla

Hace unos meses me encontré con el problema de tener que realizar páginas HTML de texto puro sin poder incrustar imágenes. Sin embargo, había que insertar un pequeño logotipo del cliente.

El problema puede resolverse convirtiendo la imagen del logotipo en una gran tabla HTML de celdas de tamaño de 1 pixel por 1 pixel y cada una del color de fondo de su pixel asociado.

En la imagen de este post puede verse el logotipo en imagen PNG, en este caso de ejemplo mi propio logotipo, no el del cliente, y abajo la misma imagen como tabla.

Como para esta publicación no he utilizado el logotipo original de la empresa que me lo encargó, sino el mio, y para simplificar el ejemplo sólo he utilizado 3 colores en la tabla, es por eso que se nota el pixelado. Pero si se usan más colores el resultado puede se muy parecido al logotipo original en imagen.

En estos casos, y limitándome a 16 colores, suelo convertir las imágenes en formato BMP (bitmap) a especificaciones en texto utilizando el conversor Bmp2Ico realizado por Juan Ramón Miráz y por mi.

Incluyo dos realizaciones del mismo código fuente: con CSS sólo para la página y con CSS para las celdas de la tabla por lo que el tamaño del código a 1/3. Como página HTML de resultado sólo incluyo en el PDF la última, por ser más reducida.

A continuación se muestra parte del código TOL utilizado para esta conversión, aunque reducido en la parte del mapa de letras del logotipo para no saturar el post. En el código PDF que se adjunta a este post se puede ver el código fuente completo.


Set repTab = 
[[
  [["+", "<td class='r'></td>"]],
  [[".", "<td class='b'></td>"]],
  [["-", "<td class='s'></td>"]]
]];

Set picSet = [[
"+++++++-..................++++++++++++++++++++++++++++++++++",
"++++++-................+++++++++++++++++++++++++++++++++++++",
"++++++-...............++++++++++++++++++++++++++++++++++++++",
"+++++-..............++++++++++++++++++++++++++++++++++++++++",
... recortado ...
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
]];

Set repCic = EvalSet(picSet, Text(Text lin)
{ // Replace 1 time
  "    <tr>\n      "+ReplaceTable(lin, repTab, 1)+"\n    </tr>\n" 
});

Text outHtm =
"
<html>
<head>
<style type='text/css'>
body { margin: 20px 20px 20px 20px; background-color: #ffffff; }
td.b { width: 1px; height: 1px; background-color: #ffffff; }
td.r { width: 1px; height: 1px; background-color: #ff0000; }
td.s { width: 1px; height: 1px; background-color: #ff9090; }
</style>
<title>Imagen en tabla</title>
</head>
<body>
  <img src='logo.bmp'>
  <br>
  <br>
  <br>
  <table bgcolor='+ffffff' cellpadding=0 cellspacing=0 border=0>
"+
  BinGroup("+",repCic)+
"
  </table>
</body>
</html>
";

Text WriteFile("logocss.htm", outHtm);
  

Lenguaje simbólico azul

Plana con una cuadro de símbolos

En los setenta e inicios de los ochenta escribía en dos lenguajes, que aunque yo los consideraba hermanos, uno más de uso habitual y otro de uso más culto, tenían características y orígenes súmamente diferentes. Con el paso del tiempo y la dificultad de evolucionar, el lenguaje más culto e icónico fue desapareciendo de mi mente y el más habitual estilizándose en mi mano.

Hacia finales de los noventa e inicios del dos mil, el lenguaje icónico había desaparecido, ya no lo sabía escribir y prácticamente no lo podía leer, mientras que el otro se descompuso a su vez en dos, el que seguía escribiendo a mano ya con cierta dificultad y su evolución simbólica, curvilínea y automatizada, esto es, que yo no lo se escribir, aunque si leer, pero que se escribir programas que lo escriben.

Aunque cuando empecé a mecanizar este lenguaje simbólico ya existían tipos de letras escalables de Adobe y Apple y aunque estos símbolos están originalmente definidos como un conjunto de curvas, estas curvas se pasan a un formato gráficos que se escala ya como un mapa de bits, perdiéndose toda posibilidad vectorial. Para automatizar el manejo de estos gráficos he confiado tradicionalmente en Alchemy (alchlong.exe) de Handmade Software, invocándolo desde el lenguaje TOL que es en el lenguaje en el que se programan los algoritmos de escritura. A continuación muestro esta invocación, para dos usos diferentes de alchlong.exe desde TOL:


///////////////////////////////////////////////////////////////////
Set AlcSize(Text filPth) // File path
///////////////////////////////////////////////////////////////////
{
  If(Not(FileExist(filPth)), [[0,0]], // File doesn't exist
  {
    Text tmpDos = "tmp\\alchemy.tmp"; // Store alchemy information
    Text inpDos = "tmp\\smb.imb"; // Alchemy don't handle complex
    Real FilCopy(filPth, inpDos); // file names

    Real sysExe = System("bin\\alchlong.exe "+ // Executable
                         "-x "+                // Options
                         inpDos+" > "+         // Input file path
                         tmpDos);              // Output file path
    Text tmpTxt = ReadFile(tmpDos);
    Text sizTxt = TxtBetween2Tag(tmpTxt,
                                 "Width x Height: ","\n",TRUE);
    If(sizTxt=="", [[0,0]], // Size doesn't exist
    {
      Set  sizSet = Tokenizer(sizTxt,"x"); // width x height
      Real width  = Eval(sizSet[1]+"; ");
      Real height = Eval(sizSet[2]+"; ");
      [[width, height]]
    })
  })
};
///////////////////////////////////////////////////////////////////
PutDescription(
"Returns a set (width, height) in pixels of a image.",
AlcSize);
///////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////
Real Bmp2Pic(Text bmpDir,
             Text picDir)
///////////////////////////////////////////////////////////////////
{
  Set filSet = GetDir(bmpDir)[1];
  Set filCic = EvalSet(filSet, Real(Text filNam)
  {
    Text inpFil = bmpDir+"/"+filNam;
    Text outFil = picDir+"/"+Replace(filNam,".bmp",".gif");
    FilMake(inpFil, outFil, Real(Text inpFil, Text outFil)
    {
      Text inpDos = "tmp\\smb.bmp";
      Text outDos = "tmp\\smb.gif";

      Real FilCopy(inpFil,inpDos); // Alchemy don't handle complex
                                   // file names
      Real sysExe = System("bin\\alchlong.exe "+   // Executable
                           "-g -Xb56 -+ ---t -o "+ // Options (84) 
                           inpDos+" "+          // Input file path
                           outDos);            // Output file path

      Real FilCopy(outDos,outFil);

      sysExe
    })
  });
  Card(filCic)
};
  

En la plana de lenguaje simbólico que puede verse al final de este post, los símbolos están ajustados a un cuadrado sobre una imagen de fondo que corresponde a mi acuarela llamada «requiescant in pace». Es a la vista de la plana cuando se entiende que clasifique este post más como pintura y poesía que como pura tecnología.

De BMP Windows a ICO Unix

De BMP de 16 bits de Windows a ICO Unix y a BIF

El programa de conversión de Bitmaps (BMP) de 16 bits de Windows al formato ICO (de icono) de Unix lo comenzó a desarrollar Juan Ramón Miraz, al comienzo de los noventa, y la versión final que se adjunta en este post la terminé yo.

El formato ICO de Unix especifica un mapa de bits como una matriz de caracteres ASCII, donde cada letra se asocia con el color del pixel de su misma fila y columna. Hubo una época que los bitmaps de los botones de los interfaces de usuario yo lo pintaba así, utilizando el editor vi de Unix y letra a letra. Por ejemplo, asociaba la v al verde, la b al blanco, la r al rojo, etc., para que me fuera más fácil, e iba poniendo letras, una a una, hasta que el botón quedaba bien. Para mi era una gratificante labor artesanal.

A partir de este programa también desarrollé el que convierte del formato BMP al formato BIF, que era un formato propio mio, que utilizaba en combinación con Clipper.

Aunque ahora se podrían recompilar estos dos códigos fuente para las versiones de Windows de 64 bits, sigo utilizando los ejecutables originales de 16 bits. Antes estos ejecutables de 16 bits podían correr mediante emulación en Windows de 32 bits, pero ahora el Windows de 64 bits ya no emula al de 16 bits.

Para poder seguir ejecutando estas pequeñas joyas históricas, sin necesidad de recompilarlas (y más aun en aquellos casos en el que el compilador es también de 16 bits) utilizo el emulador de x86 con DOS de DOSBox que se publica con licencia GNU GPL y aceptan donaciones (yo se la di).

Aunque adjunto en PDF el código fuente completo de los 2 programas de conversión y un ejemplo del formato ICO generado con mi logotipo personal, pongo a continuación el ciclo fundamental de extracción de pixels de un Bitmap (BMP) de 16 colores. Como curiosidad, en esta época, por razones de normalización con el equipo de software con el que trabajaba, los corchetes { } están en la vertical del bloque de código y no dos caracteres antes como es mi costumbre habitual:


for (Y=0, YMax=Bmih.biHeight; Y < YMax; Y++)
  {
  fseek(Bmp, RowStart, SEEK_SET);
  for (X=0, XMax=Bmih.biWidth; X < XMax; X++)
    {
    Arg = fgetc(Bmp);
    H = (Arg >> 4) & 0x0F;  H = Color[H];
    L = Arg & 0x0F;         L = Color[L];
    X++;
    fprintf(Pix,"%c%c",(char)H,(char)L);
    }
  RowStart -= RowBytes;
  fprintf(Pix,"\n");
  fputc('.',stdout); fflush(stdout);
  }