Table of Contents
- PDF Extraction with pi — Read, Search, and Extract Images
- Install the Tools
- Quick Overview
- Workflow 1: Extract Specific Section from a Manual
- Step 1: Get the PDF metadata
- Step 2: Extract full text and search
- Step 3: Pinpoint the exact pages
- Step 4: Find all occurrences across the manual
- Workflow 2: Extract Specific Text with Table Layout
- Workflow 3: Extract Images from PDF
- Check what images are embedded
- Extract all images
- Extract a specific page as an image (for diagrams not embedded)
- Crop a region from the rendered page
- Workflow 4: Full Deep-Dive into One Section
- Workflow 5: Cross-Reference Manual with Code Repo
- Setup
- Step 1: Extract the procedure from the manual
- Step 2: Find the matching code in the repo
- Step 3: Read and export the code
- Step 4: Cross-reference summary
- Step 5: Export everything
- Troubleshooting
- pi Commands Cheat Sheet
PDF Extraction with pi — Read, Search, and Extract Images
pi can read PDFs, extract specific sections, and pull out embedded images using standard command-line tools. No Python, no OCR — just fast native binaries.
Install the Tools
Linux (Debian/Ubuntu)
sudo apt update
sudo apt install -y poppler-utils ripgrep imagemagick
| Package | Provides | Purpose |
|---|---|---|
poppler-utils |
pdftotext, pdfinfo, pdfimages, pdftoppm |
Text extraction, metadata, image extraction, page rendering |
ripgrep |
rg |
Fast text search with context |
imagemagick |
magick (v7) or convert (v6) |
Image format conversion, cropping |
macOS
brew install poppler ripgrep imagemagick
Verify
pdftotext -v
pdfinfo -v
rg --version
magick --version
Quick Overview
PDF file
│
├─► pdfinfo manual.pdf → Metadata: pages, author, title
├─► pdftotext manual.pdf - → Full text to stdout
├─► pdftotext -layout manual.pdf → Preserve column layout
├─► pdfimages -j manual.pdf out → Extract embedded JPEG/PNG images
├─► pdftoppm manual.pdf page → Render page to PNG (for non-embedded images)
└─► magick input.jpg output.png → Convert between formats
Workflow 1: Extract Specific Section from a Manual
Step 1: Get the PDF metadata
User: tell me about this PDF: /path/to/manual.pdf
Agent runs: pdfinfo /path/to/manual.pdf
Output:
Title: HISv3 Operator Manual
Author: HIS Indonesia
Pages: 234
Page size: 595 x 842 pts (A4)
PDF version: 1.7
Step 2: Extract full text and search
User: does this manual mention "billing cancellation"?
Agent runs:
pdftotext /path/to/manual.pdf - | rg -i -C 2 "billing cancellation"
Output (with 2 lines of context):
134: 5.3 Billing Cancellation Procedure
135: To cancel a billing entry, navigate to Billing > Active Bills.
136: Select the bill, click "Cancel", and provide a reason. The system
137: will reverse all charges and mark the bill as void.
Step 3: Pinpoint the exact pages
User: extract pages 134-136 as text
Agent runs:
pdftotext -f 134 -l 136 -layout /path/to/manual.pdf -
Output:
5.3 Billing Cancellation Procedure
To cancel a billing entry, navigate to Billing > Active Bills.
Select the bill, click "Cancel", and provide a reason. The system
will reverse all charges and mark the bill as void.
5.3.1 Partial Cancellation
For partial cancellation, select individual items within the bill
and click "Cancel Selected". Only unbilled items can be cancelled.
Items already dispatched or consumed cannot be reversed.
Step 4: Find all occurrences across the manual
User: find every page that mentions "cancellation" with page numbers
Agent runs:
pdftotext /path/to/manual.pdf - | rg -n "cancellation" -i
Output:
3523: 5.3 Billing Cancellation Procedure
3551: For partial cancellation, select individual items...
7823: 12.1 Order Cancellation
9102: cancellation reason codes:
Workflow 2: Extract Specific Text with Table Layout
User: extract the table of billing codes from page 67, keep the columns
Agent runs:
pdftotext -f 67 -l 67 -layout /path/to/manual.pdf -
Output:
Code Description Price Category
B001 General Consultation 150,000 OPD
B002 Specialist Consultation 250,000 OPD
B003 Emergency Room Visit 350,000 ER
B004 Inpatient Daily Room 500,000 IPD
B005 ICU Daily Rate 1,200,000 ICU
Workflow 3: Extract Images from PDF
Check what images are embedded
User: what images are inside this PDF?
Agent runs: pdfimages -list /path/to/manual.pdf
Output:
page num type width height color comp bpc enc
---- --- ---- ----- ------ ----- ---- --- ---
1 0 image 800 600 rgb 3 8 jpeg
1 1 image 200 150 gray 1 8 jpeg
45 2 image 1200 900 rgb 3 8 jpeg
67 3 image 400 300 rgb 3 8 jpeg
89 4 image 600 400 rgb 3 8 png
Extract all images
mkdir -p images
pdfimages -j /path/to/manual.pdf images/img
-j writes JPEG images as .jpg (not .ppm). PNG images get .png.
ls images/
img-000.jpg img-001.jpg img-002.jpg img-003.jpg img-004.png
Extract a specific page as an image (for diagrams not embedded)
Not everything in a PDF is an embedded image — diagrams and screenshots may be vector graphics. Render the page to PNG:
User: render page 67 as an image
Agent runs:
pdftoppm -f 67 -l 67 -r 150 -png /path/to/manual.pdf page67
Output:
page67-67.png (150 DPI, full A4 page)
Crop a region from the rendered page
User: just the billing codes table from that page image
Agent crops using ImageMagick:
magick page67-67.png -crop 800x400+100+300 cropped-table.png
Output:
cropped-table.png (800x400px, just the table area)
Workflow 4: Full Deep-Dive into One Section
User: I need everything about "admission workflow" from the hospital manual.
Give me text, find images, and tell me which pages.
Agent does 4 things:
1. Metadata:
pdfinfo /path/to/manual.pdf
→ 234 pages, A4
2. Find section boundaries:
pdftotext /path/to/manual.pdf - | rg -n -i "admission workflow"
→ Lines 4521-4890 mention "admission workflow"
3. Extract the section text:
pdftotext -f 30 -l 34 -layout /path/to/manual.pdf -
→ Full text of the admission workflow section
4. Extract images from those pages:
pdfimages -f 30 -l 34 -j /path/to/manual.pdf images/admission
→ admission-003.jpg, admission-004.png
And render pages as images:
pdftoppm -f 30 -l 34 -r 150 -png /path/to/manual.pdf pages/admission
→ pages/admission-30.png through admission-34.png
Agent reports:
Section "Admission Workflow" spans pages 30-34 (5 pages)
2 embedded images extracted
5 page renders saved for visual reference
Full text ready for analysis
Workflow 5: Cross-Reference Manual with Code Repo
[!] Disclaimer: All code in this example is fictional and for demonstration purposes only. It does not represent any real codebase, API, or database schema. Use the techniques shown here against your own PDFs and repositories.
Realistic scenario: you have a hospital HIS operation manual (PDF) and the corresponding source code. pi extracts the manual's specs, finds matching code, and exports everything.
Setup
User: I have the HIS Operator Manual at docs/manual.pdf and the backend code at ./
Cross-reference the "Billing Cancellation" procedure from the manual
with the actual code. Find the API endpoint, GraphQL mutation,
storage query, and any validation logic. Export everything.
Step 1: Extract the procedure from the manual
# Find where billing cancellation is documented
pdftotext docs/manual.pdf - | rg -n -i "billing cancellation"
# Line 4521-4602 → around page 134
# Extract pages 133-136 with layout
pdftotext -f 133 -l 136 -layout docs/manual.pdf -
Extracted manual text (cleaned):
5.3 Billing Cancellation Procedure
Endpoint: POST /api/v1/billing/cancel
Request:
- bill_id: string (required) — UUID of the bill
- items: array of item_id (optional) — specific items to cancel
- reason: string (required) — cancellation reason code
Business Rules:
- Only bills with status "active" or "draft" can be cancelled
- Cancelled items must not be already dispatched
- Partial cancellation allowed only if bill is "active"
- Reason code must be from master_reason_codes table
- Cancellation triggers stock reversal for pharmacy items
Response:
- success: boolean
- cancelled_items: array
- reversal_transactions: array
Step 2: Find the matching code in the repo
Agent searches the codebase using extracted keywords:
# Find the API endpoint
rg -l "billing.*cancel" internal/
→ internal/service/billing/cancel.go
→ internal/storage/billing/cancel.go
→ internal/handler/billing_handler.go
# Find the GraphQL mutation
rg -l "cancelBilling\|CancelBilling" src/
→ src/modules/billing/mutations/cancelBilling.ts
# Find the route registration
rg "billing/cancel" internal/handler/
→ handler.POST("/api/v1/billing/cancel", h.CancelBilling)
Step 3: Read and export the code
Handler — internal/handler/billing_handler.go:
// POST /api/v1/billing/cancel
func (h *BillingHandler) CancelBilling(c *gin.Context) {
var req dto.CancelBillingRequest
if err := c.ShouldBindJSON(&req); err != nil {
respondError(c, http.StatusBadRequest, "invalid request")
return
}
result, err := h.svc.CancelBilling(c.Request.Context(), req)
if err != nil {
respondError(c, codeFromErr(err), err.Error())
return
}
respondOK(c, result)
}
Service — internal/service/billing/cancel.go:
func (s *BillingService) CancelBilling(ctx context.Context, req dto.CancelBillingRequest) (*dto.CancelBillingResponse, error) {
// Rule 1: Only active or draft bills
bill, err := s.storage.GetBill(ctx, req.BillID)
if err != nil {
return nil, fmt.Errorf("bill not found: %w", err)
}
if bill.Status != "active" && bill.Status != "draft" {
return nil, fmt.Errorf("bill status %s cannot be cancelled", bill.Status)
}
// Rule 2: Validate reason code
reason, err := s.storage.GetReasonCode(ctx, req.Reason)
if err != nil || !reason.IsActive {
return nil, fmt.Errorf("invalid reason code: %s", req.Reason)
}
// Rule 3: Check dispatched items
if req.Items != nil {
for _, itemID := range req.Items {
item, _ := s.storage.GetBillItem(ctx, itemID)
if item.IsDispatched {
return nil, fmt.Errorf("item %s already dispatched", itemID)
}
}
}
// Execute cancellation + stock reversal
return s.storage.CancelBilling(ctx, req)
}
Storage — internal/storage/billing/cancel.go:
func (s *BillingStorage) CancelBilling(ctx context.Context, req dto.CancelBillingRequest) (*dto.CancelBillingResponse, error) {
tx, err := s.db.Begin(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback(ctx)
// Cancel bill items
query := `
UPDATE billing_items
SET status = 'cancelled', cancelled_at = NOW(), cancelled_by = $4
WHERE bill_id = $1 AND status = 'active'
AND ($2::uuid[] IS NULL OR id = ANY($2))`
_, err = tx.Exec(ctx, query, req.BillID, req.Items, req.Reason, req.UserID)
if err != nil {
return nil, fmt.Errorf("cancel billing items: %w", err)
}
// Reverse pharmacy stock
_, err = tx.Exec(ctx, `
UPDATE pharmacy_stock ps
SET quantity = ps.quantity + bi.quantity
FROM billing_items bi
WHERE bi.bill_id = $1
AND bi.item_type = 'pharmacy'
AND ps.drug_id = bi.drug_id`, req.BillID)
if err != nil {
return nil, fmt.Errorf("reverse stock: %w", err)
}
return &dto.CancelBillingResponse{
Success: true,
CancelledItems: cancelledItems,
ReversalTransactions: reversals,
}, tx.Commit(ctx)
}
Frontend — src/modules/billing/mutations/cancelBilling.ts:
export const CANCEL_BILLING = graphql(`
mutation CancelBilling($input: CancelBillingInput!) {
cancelBilling(input: $input) {
success
cancelledItems {
id
name
quantity
}
reversalTransactions {
id
drugId
quantity
}
}
}
`);
DTO — internal/dto/billing.go:
type CancelBillingRequest struct {
BillID string `json:"bill_id" binding:"required,uuid"`
Items []string `json:"items"`
Reason string `json:"reason" binding:"required"`
UserID string `json:"-"`
}
type CancelBillingResponse struct {
Success bool `json:"success"`
CancelledItems []CancelledItem `json:"cancelled_items"`
ReversalTransactions []ReversalTransaction `json:"reversal_transactions"`
}
Step 4: Cross-reference summary
Agent produces a traceability table linking manual to code:
| Manual Spec | Code Location | Match? |
|---|---|---|
Endpoint POST /api/v1/billing/cancel |
internal/handler/billing_handler.go:45 |
[+] Match |
bill_id required UUID |
dto.CancelBillingRequest.BillID with binding:"required,uuid" |
[+] Match |
| Only active/draft status | service/cancel.go:12 — bill.Status != "active" && bill.Status != "draft" |
[+] Match |
| Reason code validation | service/cancel.go:18 — GetReasonCode lookup |
[+] Match |
| Stock reversal for pharmacy | storage/cancel.go:30 — UPDATE pharmacy_stock |
[+] Match |
| Items must not be dispatched | service/cancel.go:24 — item.IsDispatched check |
[+] Match |
| Partial cancellation | items optional in DTO + $2::uuid[] IS NULL in SQL |
[+] Match |
| GraphQL mutation | mutations/cancelBilling.ts — cancelBilling(input) |
[+] Match |
Step 5: Export everything
# Export all findings to a report
mkdir -p export/billing-cancellation
# Export manual section
pdftotext -f 133 -l 136 -layout docs/manual.pdf export/billing-cancellation/manual-section.txt
# Export code snippets
rg -C 5 "CancelBilling\|cancel.*billing" internal/ --type go > export/billing-cancellation/backend-code.txt
rg -C 5 "cancelBilling\|CANCEL_BILLING" src/ --type ts > export/billing-cancellation/frontend-code.txt
# Export the SQL query
rg -C 10 "UPDATE billing_items.*cancelled" internal/storage/ > export/billing-cancellation/sql-query.sql
# Export rendered pages
pdftoppm -f 133 -l 136 -r 150 -png docs/manual.pdf export/billing-cancellation/page
# Bundle as a single report
cat export/billing-cancellation/*.txt > export/billing-cancellation/full-report.md
Agent summary:
Billing Cancellation — Manual vs Code Cross-Reference
All 8 manual specifications have matching code implementations.
Files exported to export/billing-cancellation/:
manual-section.txt — Original manual text (pages 133-136)
backend-code.txt — Handler, service, storage, DTO
frontend-code.txt — GraphQL mutation
sql-query.sql — Cancellation + stock reversal queries
page-133.png..136.png — Rendered manual pages
full-report.md — Combined report
No gaps found between documentation and code.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
pdftotext: command not found |
poppler-utils not installed | sudo apt install poppler-utils |
rg: command not found |
ripgrep not installed | sudo apt install ripgrep |
magick: command not found |
ImageMagick v7 | Use convert for v6 or install v7 |
| Extracted text is garbled | PDF is scanned (image-based) | Use OCR tool instead: tesseract |
pdfimages extracts no files |
Images are vector, not raster | Use pdftoppm to render the page |
pdfimages -list shows smask type |
Image has soft mask | Re-run without -j flag |
| Text search misses results | PDF uses ligatures/fancy fonts | Try pdftotext -raw (no layout) |
pi Commands Cheat Sheet
# Metadata
@bash pdfinfo file.pdf
# Full text dump
@bash pdftotext file.pdf -
# Layout-preserved text (tables)
@bash pdftotext -layout file.pdf -
# Specific page range
@bash pdftotext -f 10 -l 15 -layout file.pdf -
# Search with context
@bash pdftotext file.pdf - | rg -i -C 3 "keyword"
# Search with page line numbers
@bash pdftotext file.pdf - | rg -n -i "keyword"
# List embedded images
@bash pdfimages -list file.pdf
# Extract all images
@bash pdfimages -j file.pdf images/prefix
# Extract images from page range
@bash pdfimages -f 10 -l 15 -j file.pdf images/prefix
# Render page as image
@bash pdftoppm -f 10 -l 10 -r 150 -png file.pdf page
# Crop rendered image (ImageMagick v7)
@bash magick input.png -crop WxH+X+Y output.png