Cómo trabajar con la estructura PDF en TypeScript
Esta guía muestra cómo crear navegación de documentos y estructura lógica en archivos PDF con Aspose.PDF FOSS para TypeScript: una tabla de contenido clickeable en una página, un esquema de marcadores a nivel de documento, destinos nombrados y un árbol estructural etiquetado para accesibilidad. Requiere Node.js 22 o posterior.
Guía paso a paso
Paso 1: Instalar el paquete
git clone https://github.com/aspose-pdf-foss/Aspose.PDF-FOSS-for-TypeScript.git
cd Aspose.PDF-FOSS-for-TypeScript
npm install
npm run buildVerifique la instalación importando la clase Document en un nuevo archivo TypeScript — esta línea debería resolverse sin errores una vez que el paquete esté instalado:
import { Document } from '@asposefoss/pdf';Paso 2: Importar clases necesarias
Importa Document para abrir el archivo y OutlineItem para las entradas de marcadores creadas en el Paso 4:
import { Document, OutlineItem } from '@asposefoss/pdf';Paso 3: Añadir una tabla de contenido
Page.AddTOC() recibe una matriz de entradas { title, page }, un rect delimitador y opciones de estilo; dibuja líneas punteadas y enlaces clicables a las páginas de destino:
const page = doc.Pages[0];
page.AddTOC(
[
{ title: '1. Introduction', page: 2 },
{ title: '2. Methodology', page: 5 },
{ title: '3. Results', page: 9 },
],
[72, 160, 400, 600],
{ font: 'Helvetica', fontSize: 13, color: [0.1, 0.1, 0.15], rowGap: 18 },
);Paso 4: Añadir marcadores
Document.SetOutlines() sustituye el panel de marcadores del documento por una matriz de objetos OutlineItem. Cada elemento puede contener un Dest (destino de página), estilos (Color, Bold) y un Children anidado para un árbol colapsable:
const items: OutlineItem[] = [
{ Title: 'Sales', Dest: { name: 'section.sales' }, Open: true, Color: [0.1, 0.4, 0.1] },
{ Title: 'Appendix', Dest: { name: 'section.appendix' } },
];
doc.SetOutlines(items);Paso 5: Leer esquemas y destinos nombrados
Document.GetOutlines() devuelve el árbol actual de marcadores. Document.GetNamedDestinations() devuelve cada destino nombrado registrado en el documento — útil para verificar que cada objetivo de esquema y cada enlace de la tabla de contenido realmente se resuelvan a una página real:
const destNames = new Set(doc.GetNamedDestinations().map((d) => d.name));
const walk = (nodes: OutlineItem[]): void => {
for (const item of nodes) {
if (item.Dest && 'name' in item.Dest) {
console.log(destNames.has(item.Dest.name), item.Title);
}
if (item.Children) walk(item.Children);
}
};
walk(doc.GetOutlines());Paso 6: Construir un árbol estructural etiquetado
Document.GetStructTree() devuelve el árbol de estructura existente (o null si el documento no ha sido etiquetado). StructTreeRoot.Append(tag) agrega un elemento hijo como 'H2' o 'P' y devuelve un StructElement, y StructElement.MarkContent(page, quad) asocia ese elemento con el contenido de la página que describe:
function tagPage(doc: Document, page: Page, headingText: string): void {
const root = doc.GetStructTree();
if (!root) throw new Error('tagPage: the document is not tagged yet');
for (const block of page.GetStructuredText()) {
const text = block.text.trim();
if (text.length === 0) continue;
const el = root.Append(text.startsWith(headingText) ? 'H2' : 'P');
el.MarkContent(page, block.quad);
}
}Problemas comunes y correcciones
Document.GetStructTree() returns null. El documento aún no está etiquetado — llame Document.CreateStructTree() (o Document.AutoTag()) antes de crear manualmente elementos con StructTreeRoot.Append() / StructElement.MarkContent().
Un elemento del esquema no salta a la página correcta. OutlineItem.Dest debe nombrar un destino que realmente exista — verifique item.Dest.name against Document.GetNamedDestinations() antes de guardar, como se muestra en el Paso 5.
Page.AddTOC() las entradas apuntan a la página incorrecta. page en cada entrada del TOC es el número de página de la propia página de destino — confírmelo contra page.Number en el objetivo real Page, no un índice de matriz supuesto.
Los marcadores desaparecen después de un Document.SetOutlines() llamado. SetOutlines() reemplaza todo el árbol de marcadores — lee el árbol actual con Document.GetOutlines() primero, modifica la matriz, luego pasa la matriz completa de vuelta si los marcadores se están añadiendo en lugar de reemplazarse.
Preguntas frecuentes
¿Puede un marcador abrirse ya expandido?
Sí — establezca Open: true en el OutlineItem para que los hijos de ese nodo sean visibles por defecto en el panel de marcadores.
¿Cuál es la diferencia entre un marcador y un destino nombrado?
Un marcador (OutlineItem) es una entrada visible en el panel de navegación; un destino nombrado (Document.GetNamedDestinations() / SetNamedDestination()) es un objetivo de página interno y reutilizable al que un marcador, una entrada de TOC o una anotación de enlace pueden apuntar por su nombre.
¿El etiquetado de un documento afecta su apariencia?
No — un árbol de estructura (StructTreeRoot, StructElement) es una capa lógica paralela que describe el orden de lectura y la semántica; no cambia el contenido visible de la página.
¿Puedo anidar marcadores?
Sí — establezca Children en un OutlineItem a una matriz de objetos OutlineItem adicionales para crear un árbol colapsable de varios niveles.