Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Extract text from complex documents using Chandra OCR — handles tables, forms, handwriting, and full page layouts with high accuracy. Use when: extracting data from scanned documents, reading complex tables from PDFs/images, processing handwritten forms.
.claude/skills/terminalskills-chandra-ocr/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 15% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-20 | ✗→✓ | ▲ Improved | 4% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 10% | 0% |
| case-05 | ✗→✓ | ▲ Improved | -23% | 0% |
Extract text from complex documents — tables, forms, handwriting, and full page layouts — using Chandra, a high-accuracy OCR engine built for real-world document complexity.
Chandra OCR handles the document types that trip up standard OCR: multi-column tables with merged cells, mixed print and handwriting, and complex page layouts. It outputs structured data (DataFrames, JSON) and supports GPU acceleration for batch processing.
bashpip install chandra-ocr
For GPU acceleration (recommended for batch processing):
bashpip install chandra-ocr[gpu]
pythonfrom chandra import OCR ocr = OCR() result = ocr.read("document.png") print(result.text) # From a PDF result = ocr.read("report.pdf") for page in result.pages: print(f"--- Page {page.number} ---") print(page.text)
pythonresult = ocr.read("document.png", preserve_layout=True) for block in result.blocks: print(f"Type: {block.type}") # paragraph, table, header, handwriting print(f"Text: {block.text}") print(f"Confidence: {block.confidence:.2f}")
pythonresult = ocr.read("invoice.png", extract_tables=True) for table in result.tables: print(f"Table: {table.rows} rows x {table.cols} columns") df = table.to_dataframe() print(df.head()) table.to_csv("extracted_table.csv")
pythonresult = ocr.read("handwritten_form.jpg", mode="handwriting") for block in result.blocks: if block.type == "handwriting": print(f"Handwritten: {block.text} (conf: {block.confidence:.2f})")
pythonresult = ocr.read("filled_form.png", mode="mixed") for block in result.blocks: print(f"[{block.type}] {block.text} (conf: {block.confidence:.2f})")
pythonimport glob from chandra import OCR import json ocr = OCR(device="cuda") files = glob.glob("documents/*.pdf") for file_path in files: result = ocr.read(file_path, extract_tables=True) output = { "file": file_path, "pages": len(result.pages), "text": result.text, "tables": [t.to_dict() for t in result.tables], } with open(file_path.replace(".pdf", ".json"), "w") as f: json.dump(output, f, indent=2)
pythonfrom chandra import OCR ocr = OCR() result = ocr.read("invoice-2025-0342.pdf", extract_tables=True) for i, table in enumerate(result.tables): df = table.to_dataframe() df.to_csv(f"invoice_table_{i}.csv", index=False) print(f"Table {i}: {table.rows} rows — columns: {list(df.columns)}") # Output: # Table 0: 12 rows — columns: ['Item', 'Qty', 'Unit Price', 'Total'] # Table 1: 3 rows — columns: ['Tax Type', 'Rate', 'Amount']
pythonfrom chandra import OCR import requests ocr = OCR() result = ocr.read("patient_intake_form.jpg", mode="mixed", extract_tables=True) extracted = {} for block in result.blocks: extracted[block.label] = { "value": block.text, "confidence": block.confidence, "needs_review": block.confidence < 0.85, } review_fields = {k: v for k, v in extracted.items() if v["needs_review"]} print(f"Fields needing review: {list(review_fields.keys())}") # Output: # Fields needing review: ['allergies', 'signature']
device="cuda" for batch processing — 5-10x faster than CPUdpi=300 or higher for scanned documents to improve accuracymode="mixed" to detect both print and marks| Option | Default | Description | |--------|---------|-------------| | mode | "auto" | Detection mode: auto, print, handwriting, mixed | | preserve_layout | False | Maintain spatial positioning of text | | extract_tables | False | Detect and extract tables as structured data | | device | "cpu" | Processing device: cpu or cuda | | language | "en" | Primary language hint | | dpi | 300 | DPI for PDF rasterization |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 7,574 | 1,769 | -77% | 1 | 1 | 0% | 1,294 | 1,551 | +20% | 0 | 0 | — |
case-02 | fail→pass | 7,290 | 2,257 | -69% | 1 | 1 | 0% | 1,443 | 1,658 | +15% | 0 | 0 | — |
case-03 | fail→pass | 8,124 | 3,355 | -59% | 1 | 1 | 0% | 1,601 | 2,008 | +25% | 0 | 0 | — |
case-20 | fail→pass | 15,031 | 8,412 | -44% | 1 | 1 | 0% | 3,228 | 3,350 | +4% | 0 | 0 | — |
case-04 | fail→pass | 8,697 | 2,528 | -71% | 1 | 1 | 0% | 1,689 | 1,851 | +10% | 0 | 0 | — |
case-05 | fail→pass | 12,395 | 3,506 | -72% | 1 | 1 | 0% | 2,454 | 1,884 | -23% | 0 | 0 | — |
case-06 | fail→pass | 14,058 | 5,792 | -59% | 1 | 1 | 0% | 2,734 | 2,425 | -11% | 0 | 0 | — |
case-07 | pass→pass | 6,633 | 3,795 | -43% | 1 | 1 | 0% | 1,246 | 2,051 | +65% | 0 | 0 | — |
case-08 | pass→pass | 10,419 | 5,899 | -43% | 1 | 1 | 0% | 2,145 | 2,543 | +19% | 0 | 0 | — |
case-09 | fail→pass | 8,539 | 2,545 | -70% | 1 | 1 | 0% | 1,723 | 1,846 | +7% | 0 | 0 | — |
case-10 | pass→fail | 12,720 | 5,225 | -59% | 1 | 1 | 0% | 2,389 | 2,176 | -9% | 0 | 0 | — |
case-11 | pass→pass | 12,318 | 2,902 | -76% | 1 | 1 | 0% | 2,434 | 1,775 | -27% | 0 | 0 | — |
case-12 | fail→pass | 7,908 | 3,726 | -53% | 1 | 1 | 0% | 1,464 | 2,000 | +37% | 0 | 0 | — |
case-13 | pass→pass | 7,338 | 3,385 | -54% | 1 | 1 | 0% | 1,293 | 1,865 | +44% | 0 | 0 | — |
case-14 | pass→pass | 9,662 | 3,061 | -68% | 1 | 1 | 0% | 1,860 | 1,930 | +4% | 0 | 0 | — |
case-15 | pass→pass | 14,189 | 11,608 | -18% | 1 | 1 | 0% | 2,738 | 3,579 | +31% | 0 | 0 | — |
case-21 | pass→pass | 4,077 | 3,261 | -20% | 1 | 1 | 0% | 869 | 1,892 | +118% | 0 | 0 | — |
case-16 | fail→pass | 9,759 | 2,495 | -74% | 1 | 1 | 0% | 1,857 | 1,851 | -0% | 0 | 0 | — |
case-17 | fail→pass | 12,886 | 2,161 | -83% | 1 | 1 | 0% | 2,323 | 1,719 | -26% | 0 | 0 | — |
case-18 | fail→pass | 11,378 | 6,931 | -39% | 1 | 1 | 0% | 2,282 | 2,886 | +26% | 0 | 0 | — |
case-19 | fail→pass | 20,579 | 9,215 | -55% | 1 | 1 | 0% | 4,115 | 3,286 | -20% | 0 | 0 | — |
case-22 | pass→pass | 16,564 | 11,124 | -33% | 1 | 1 | 0% | 3,453 | 3,732 | +8% | 0 | 0 | — |
case-23 | pass→pass | 8,016 | 3,146 | -61% | 1 | 1 | 0% | 1,682 | 1,917 | +14% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +48 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.