Todo lo que flota sobre la cuadrícula de una hoja de cálculo (un gráfico, un logotipo, un sello, un cuadro de llamada) es un objeto de dibujo, y un objeto de dibujo se define por dos cosas: qué es y dónde está anclado. El ancla es la parte en la que la gente se equivoca. Un gráfico no vive en una celda; se asienta en un rectángulo fijado a un tramo de filas y columnas, y los datos que grafica son un conjunto separado de referencias A1 del que el ancla no sabe nada. Mueva el marco y el trazado se queda donde está. Inserte filas debajo y el marco se desliza hacia abajo con ellas. Mantener claros esos dos sistemas de coordenadas es la mayor parte de lo que hace que el código de dibujo se comporte
HotXLS es una biblioteca nativa en Object Pascal que lee y escribe XLS y XLSX sin automatización de Excel, y lleva dos modelos de dibujo separados porque los dos formatos de archivo almacenan los dibujos de forma distinta. El formato BIFF8 .xls mantiene los gráficos en sus propias hojas dedicadas y las formas flotantes en un flujo OfficeArt adjunto a la hoja de cálculo. El formato OOXML .xlsx puede incrustar un gráfico dentro de la cuadrícula, anclado a un rectángulo de celdas, junto con el mismo tipo de imágenes y formas flotantes. El modelo de objetos refleja esa división, y los fallos sobre los que vale la pena escribir provienen todos de aplicar las reglas de un formato al otro
Qué contenedor puede contener qué
La elección del contenedor tiene que hacerse antes de cualquier código de gráficos, porque los tipos de objeto disponibles difieren entre los dos:
- XLS (BIFF8): los gráficos viven en hojas de gráfico dedicadas creadas mediante
AddChartSheeten la colecciónSheets. Las imágenes, cuadros de texto, rectángulos, óvalos y líneas son formas OfficeArt gestionadas a través de la colecciónShapesde la hoja de cálculo. No hay API para incrustar un gráfico dentro de la cuadrícula de una hoja de cálculo normal - XLSX (OOXML): los gráficos pueden incrustarse directamente en una hoja de cálculo con
TXLSXWorksheet.AddChart, anclados a un rectángulo de celdas, o colocarse en una hoja de gráfico dedicada conTXLSXWorkbook.AddChartSheet. Las imágenes entran conAddImageoAddImageFromFile, y las etiquetas flotantes conAddTextBox
Así que un requisito formulado como "una hoja de tablero con el gráfico junto a los números" es en realidad un requisito de .xlsx. Solo puede aproximarlo en .xls empujando el gráfico a su propia hoja, lo que cambia cómo el usuario navega el archivo y cambia cómo debe comportarse su código. La hoja que devuelve el AddChartSheet del lado XLS es un substream de gráfico, no una cuadrícula: escribir en ella con Cells.Item produce un flujo de dibujo inconsistente que se genera sin error y que Excel luego descarta al abrir. El gráfico simplemente desaparece, y nada en el registro de compilación dice por qué. Trate la hoja devuelta como solo de gráfico y toda la clase de reportes de "gráfico faltante" se esfuma
Incrustar un gráfico en una hoja de cálculo XLSX
La ruta XLSX es la que tiene margen de maniobra, y es donde los dos sistemas de coordenadas de la introducción se vuelven concretos. El rectángulo de anclaje que se pasa a AddChart se expresa en filas y columnas de la hoja de cálculo y fija dónde se asienta el marco del gráfico. Los datos de las series se expresan como referencias A1 absolutas que incluyen el nombre de la hoja. Son independientes: puede mover el marco al otro extremo de la hoja y seguirá graficando las mismas celdas
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Chart: TXLSXChart;
begin
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Sales');
Sheet.Cells[1, 1].Value := 'Region';
Sheet.Cells[1, 2].Value := 'Revenue';
Sheet.Cells[2, 1].Value := 'East';
Sheet.Cells[2, 2].Value := 1184350;
Sheet.Cells[3, 1].Value := 'Central';
Sheet.Cells[3, 2].Value := 902210;
Sheet.Cells[4, 1].Value := 'West';
Sheet.Cells[4, 2].Value := 1010675;
// Marco anclado a las filas 6..22, columnas 1..8
Chart := Sheet.AddChart(xlsxChartColumn, 'Revenue by Region', 6, 1, 22, 8);
Chart.AddSeries('Revenue', 'Sales!$A$2:$A$4', 'Sales!$B$2:$B$4');
Chart.ValueAxisTitle := 'USD';
Sheet.AddImageFromFile(1, 5, 'logo.png');
Book.SaveAs('dashboard.xlsx');
finally
Book.Free;
end;
end;
El argumento que muerde es la cadena de rango que se entrega a AddSeries. Es un literal, capturado en el momento de la llamada, y no tiene idea de que usted podría anexar veinte filas más de datos después. Constrúyala a partir de un conteo de filas que calculó después de escribir los datos, nunca antes. Los gráficos de dispersión y de burbujas sobrecargan los mismos dos argumentos con significados distintos: el rango de categorías ahora suministra los valores X y el rango de valores suministra Y, y el radio de la burbuja proviene de una tercera referencia fijada mediante BubbleSizeRange en el TXLSXChartSeries devuelto. Lea la llamada como "X, Y, tamaño" en lugar de "categorías, valores" en cuanto salga de la familia de columnas y barras
TXLSXChartType abarca gráficos de columnas, barras, líneas, circulares, de área, de anillo, de dispersión, de burbujas y radiales, lo que cubre el repertorio cotidiano de informes. Para un gráfico a página completa sin cuadrícula alrededor, Book.AddChartSheet devuelve una hoja cuya propiedad IsChartSheet es verdadera. Es la contraparte .xlsx de la hoja de gráfico heredada y lleva la misma expectativa: no escriba contenido de celdas en ella
Las imágenes entran como bytes, y se dimensionan en EMU
Hay dos sobrecargas para insertar una imagen, y confundirlas es el error de imágenes que más aparece en las revisiones de código. AddImage(ARow, ACol, AData, AFormat) quiere los bytes de la imagen ya codificados en AData: el contenido sin procesar de un PNG, JPEG, GIF o BMP. Pásele una ruta de archivo y habrá almacenado una cadena de cuarenta bytes que ningún visor puede decodificar, que es exactamente el reporte del icono de imagen rota que no quiere depurar después del despliegue. Cuando el origen es un archivo en disco, llame a AddImageFromFile en su lugar y deje que la biblioteca lea los bytes y clasifique el formato por usted
Luego viene el dimensionamiento. DrawingML no mide en píxeles; mide en English Metric Units, donde 914400 EMU hacen una pulgada y, a 96 DPI, 9525 EMU hacen un píxel. El objeto TXLSXImage expone WidthEMU y HeightEMU, así que un logotipo pensado para renderizarse a 180 por 60 píxeles necesita 1714500 por 571500 EMU. Ponga esa conversión en una constante con nombre y calcule contra ella. Los números mágicos como 1714500 dispersos por el código son ilegibles y quedan silenciosamente mal la primera vez que alguien cambia el DPI objetivo. La fila y la columna de anclaje, por cierto, tienen base 1, coincidiendo con el resto de la API de celdas y no con la aritmética EMU de base 0
Hojas de gráfico y formas en archivos XLS heredados
Del lado BIFF8, la sobrecarga más completa de AddChartSheet recibe el tipo de gráfico, los títulos de los ejes y un arreglo abierto de registros TXLSChartSeriesInfo, donde cada registro contiene un nombre y un rango de categorías y de valores como cadenas. Las formas flotantes son un asunto aparte: van en la propia hoja de datos, a través de su colección Shapes, no en la hoja de gráfico
var
Book: IXLSWorkbook;
Data, Trend: IXLSWorksheet;
Series: array[0..0] of TXLSChartSeriesInfo;
begin
Book := TXLSWorkbook.Create; // contado por interfaz: no llame a Free
Data := Book.Sheets.Add;
Data.Name := 'Data';
Data.Cells.Item[1, 1].Value := 'Month';
Data.Cells.Item[1, 2].Value := 'Units';
Data.Cells.Item[2, 1].Value := 'Apr';
Data.Cells.Item[2, 2].Value := 1530;
Data.Cells.Item[3, 1].Value := 'May';
Data.Cells.Item[3, 2].Value := 1721;
Series[0].Name := 'Units';
Series[0].Categories := 'Data!$A$2:$A$3';
Series[0].Values := 'Data!$B$2:$B$3';
Trend := Book.Sheets.AddChartSheet('Trend', xlsChartTypeLine,
'Units sold', 'Month', 'Units', Series);
// Trend es un substream de gráfico: nunca llame métodos de celda sobre él
Data.Shapes.AddTextBox('Source: ERP nightly export', 6, 1, 8, 4);
Data.Shapes.AddPicture('approved-stamp.bmp');
Book.SaveAs('trend.xls');
end;
Aquí importan dos detalles de ciclo de vida, y tiran en direcciones opuestas. TXLSWorkbook se sostiene a través de la interfaz IXLSWorkbook y tiene conteo de referencias, así que llamar a Free sobre él por su cuenta provoca una doble liberación. TXLSXWorkbook, de las secciones anteriores, es un objeto normal y debe liberarse en un try..finally. El mismo revisor de código que señala un Free ausente del lado XLSX tiene que señalar uno presente del lado XLS, lo que es un tropiezo genuino cuando se trabaja con ambos formatos en la misma unidad. Los ayudantes de formas en sí son uniformes: AddRectangle, AddOval y AddLine, con DeleteInRange para despejar una región de dibujos, todos anclan por pares de fila y columna, así que una plantilla que inserta filas encima de ellos los desplaza junto con la cuadrícula
Una propiedad más se gana su lugar en los archivos heredados. TXLSPicture.TransparentColor enmascara un color de fondo elegido de un mapa de bits, que es la forma de colocar un sello no rectangular (un sello de "Aprobado", una marca de agua) sobre la cuadrícula en un formato cuyo renderizado BIFF nunca aprendió el alfa de PNG. Fije el color contra el que se creó el sello y el rectángulo circundante desaparece
Los colores de tema no sobreviven a una ida y vuelta por BIFF8
Los rellenos de dibujo de OOXML pueden apuntar a una ranura de color de tema, razón por la cual recolorear un .xlsx completo cambiando su tema es barato. Los registros de dibujo de BIFF8 no tienen esa ranura. Cuando HotXLS aplica un color de tema a un dibujo XLS, resuelve el color a un valor RGB literal y almacena eso; el índice de tema del que provino desaparece en el instante en que se escribe el archivo, y reabrirlo no puede recuperarlo. Esto atrapa en particular a las herramientas de informes de marca blanca, del tipo que cambia la marca del mismo documento generado para muchos clientes. Mantenga la correspondencia de tema a RGB en su propia configuración y vuelva a aplicarla cada vez que genere, en lugar de esperar leerla de vuelta desde un .xls guardado
Una decisión relacionada aparece del lado del rendimiento. A la fachada XLS se le puede indicar que omita por completo el análisis de la capa de dibujo cuando todo lo que quiere de un archivo heredado grande son sus datos de celdas, fijando _DisableGraphics en verdadero, y eso recorta tiempo real en las lecturas masivas. La trampa es permanente: un libro de trabajo abierto de esa forma no tiene flujo OfficeArt en memoria, así que guardarlo escribe los dibujos fuera de la existencia. Reserve la bandera para tareas analíticas de solo lectura. El panorama más amplio de rendimiento está en nuestras notas sobre el rendimiento con libros de trabajo grandes en HotXLS
Mantener las anclas estables mientras la cuadrícula cambia
Los informes rara vez conservan el tamaño con el que se generaron, y aquí es donde el modelo de anclaje de la introducción rinde frutos. Las operaciones estructurales de la fachada XLSX (InsertRows, DeleteRows y los equivalentes de columna) mueven las capas dependientes junto con las celdas. Las regiones combinadas, los hipervínculos, los comentarios, los paneles inmovilizados, los rangos de filtro, los formatos condicionales, las validaciones, las tablas, los nombres definidos y, para este tema, las anclas de imágenes y gráficos viajan todos juntos. Un logotipo anclado en la fila 1 se queda arriba cuando entran diez filas debajo. Un marco de gráfico anclado bajo el bloque de datos se desliza hacia abajo a medida que el bloque crece. Lo único que no se reescribe es cualquier cadena de rango que haya capturado como literal antes de que ocurriera la inserción, ya que es solo texto que la biblioteca no tiene motivo para revisitar. Eso fija el orden seguro para llenar una plantilla: escriba y remodele primero los datos, y cree los gráficos y coloque las imágenes como pasada final, con cada cadena de rango derivada de los conteos de filas que tiene después de las inserciones, no antes
Dos herramientas menores completan el kit de colocación. TXLSTextBox.SetArea del lado XLS vuelve a anclar un cuadro de texto o autoforma existente a un nuevo rectángulo de celdas, lo que supera a eliminarlo y recrearlo cuando un bloque de pie de página se desplaza. Y la sobrecarga de mapa de bits de AddPicture recibe un TBitmap vivo con una bandera de transparencia opcional, así que cualquier cosa que su propio código VCL pueda dibujar (un indicador, una tira de minigráficos, un tipo de gráfico que la lista nativa no ofrece) puede estamparse directamente en la hoja sin escribir primero un archivo temporal
Los gráficos y las imágenes son casi siempre la capa final de un informe ya estructurado, razón por la cual el trabajo previo decide si aterrizan limpiamente. El llenado de los datos que un gráfico referenciará se cubre en generación de informes basada en plantillas, y mantener la cuadrícula estable debajo de sus anclas es el tema de celdas combinadas y control de diseño. La documentación completa de clases y métodos está en la página de producto de HotXLS Delphi Component