{"record":{"id":"785876040c4da944","repo":"ocrmypdf/OCRmyPDF","slug":"pdf-graphics-stack-overflowed-spec-limit","errorCode":null,"errorMessage":"PDF graphics stack overflowed spec limit","messagePattern":"PDF graphics stack overflowed spec limit","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/ocrmypdf/pdfinfo/_contentstream.py","lineNumber":192,"sourceCode":"    found_text = False\n    vector_ops = set(['S', 's', 'f', 'F', 'f*', 'B', 'B*', 'b', 'b*'])\n    text_showing_ops = set([\"TJ\", \"Tj\", '\"', \"'\"])\n    image_ops = set(['BI', 'ID', 'EI', 'q', 'Q', 'Do', 'cm'])\n    color_ops = set(['g', 'rg', 'k', 'cs', 'sc', 'scn'])\n    operator_whitelist = ' '.join(vector_ops | text_showing_ops | image_ops | color_ops)\n\n    for n, graphobj in enumerate(\n        _normalize_stack(parse_content_stream(contentstream, operator_whitelist))\n    ):\n        operands, operator = graphobj\n        if operator == 'q':\n            stack.append((ctm, fill_ink, fill_space))\n            if len(stack) > 32:  # See docstring\n                if len(stack) > 128:\n                    raise RuntimeError(\n                        f\"PDF graphics stack overflowed hard limit at operator {n}\"\n                    )\n                warn(\"PDF graphics stack overflowed spec limit\")\n        elif operator == 'Q':\n            try:\n                ctm, fill_ink, fill_space = stack.pop()\n            except IndexError:\n                # Keeping the state the same seems to be the only sensible thing\n                # to do. Just pretend nothing happened, keep calm and carry on.\n                warn(\"PDF graphics stack underflowed - PDF may be malformed\")\n        elif operator == 'cm':\n            try:\n                ctm = Matrix(operands) @ ctm\n            except ValueError as e:\n                raise InputFileError(\n                    \"PDF content stream is corrupt - this PDF is malformed. \"\n                    \"Use a PDF editor that is capable of visually inspecting the PDF.\"\n                ) from e\n        elif operator == 'g':\n            if vals := _operand_floats(operands):\n                fill_ink = _ink_from_components('gray', vals)","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/ocrmypdf/OCRmyPDF/blob/5074a0b0e109362422b768fd271ed84bf717c4ec/src/ocrmypdf/pdfinfo/_contentstream.py#L174-L210","documentation":"While interpreting a PDF content stream, more than 32 'q' (save-state) operators were nested without matching 'Q' (restore) — exceeding the PDF spec's graphics state stack limit. The library tolerates up to 128 before a hard RuntimeError; this warning means ink coverage analysis may be imprecise.","triggerScenarios":"Interpreting content streams (used for preprocessing decisions / page ink analysis) of PDFs that stack many q operators — typically machine-generated or malicious/abusive PDFs.","commonSituations":"PDF generators that emit long q runs; fuzzed/adversarial PDFs; the accompanying test test_stack_abuse exercises exactly this.","solutions":["The warning is non-fatal — processing continues; no action strictly required","If ink analysis misbehaves, flatten/repair the PDF first (qpdf --stream-data=uncompress, pikepdf)","Regenerate the offending PDF from its source if you control the generator"],"exampleFix":"# before\ninfo = PdfInfo.from_path(path)  # may warn on abusive PDFs\n# after\nimport pikepdf\nwith pikepdf.open(path, allow_overwriting_input=True) as pdf:\n    pdf.save(path)  # normalized copy first\ninfo = PdfInfo.from_path(path)","handlingStrategy":"fallback","validationCode":"import pikepdf\\nwith pikepdf.open(path) as pdf:\\n    pdf.save(normalized_path)  # rebalances/normalizes streams","typeGuard":null,"tryCatchPattern":"with warnings.catch_warnings():\\n    warnings.simplefilter('ignore', UserWarning, message='PDF graphics stack')\\n    process(path)","preventionTips":["Normalize machine-generated/abusive PDFs with qpdf or pikepdf first","Treat stack warnings as signals of generator bugs in your own PDF writer"],"tags":["pdf","content-stream","graphics-state","malformed-pdf"],"backgroundTag":"pdf-graphics-stack-overflow","analyzedSha":"5074a0b0e109362422b768fd271ed84bf717c4ec","analyzedAt":"2026-08-27T11:57:38.523Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}