| 771 | const visited = new Set<string>(); |
| 772 | |
| 773 | const walkNode = (nodeOrRef: PdfObject | null, currentRef?: PdfRef): void => { |
| 774 | // Handle references |
| 775 | if (nodeOrRef instanceof PdfRef) { |
| 776 | const key = `${nodeOrRef.objectNumber} ${nodeOrRef.generation}`; |
| 777 | |
| 778 | if (visited.has(key)) { |
| 779 | this.warnings.push(`Circular reference in page tree: ${key}`); |
| 780 | return; |
| 781 | } |
| 782 | |
| 783 | visited.add(key); |
| 784 | |
| 785 | const resolved = getObject(nodeOrRef); |
| 786 | |
| 787 | walkNode(resolved, nodeOrRef); |
| 788 | return; |
| 789 | } |
| 790 | |
| 791 | // Must be a dictionary |
| 792 | if (!(nodeOrRef instanceof PdfDict)) { |
| 793 | return; |
| 794 | } |
| 795 | |
| 796 | const type = nodeOrRef.getName("Type"); |
| 797 | |
| 798 | if (type?.value === "Page") { |
| 799 | // Leaf node - this is a page |
| 800 | if (currentRef) { |
| 801 | pages.push(currentRef); |
| 802 | } |
| 803 | } else if (type?.value === "Pages") { |
| 804 | // Intermediate node - recurse into kids |
| 805 | const kids = nodeOrRef.getArray("Kids", getObject); |
| 806 | |
| 807 | if (kids) { |
| 808 | for (let i = 0; i < kids.length; i++) { |
| 809 | const kid = kids.at(i); |
| 810 | |
| 811 | if (kid instanceof PdfRef) { |
| 812 | walkNode(kid); |
| 813 | } else if (kid instanceof PdfDict) { |
| 814 | walkNode(kid); |
| 815 | } |
| 816 | // Skip null/invalid kids silently |
| 817 | } |
| 818 | } |
| 819 | } |
| 820 | // Ignore nodes without proper /Type |
| 821 | }; |
| 822 | |
| 823 | // Start from the catalog's Pages reference |
| 824 | const catalog = getCatalog(); |