You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replace shell-based process runner with open3_safe
Replaces the bash pipe + system timeout binary approach in ExternalProcess#run
with Open3Safe.capture3_safe, which provides proper stdout/stderr capture,
timeout handling, and RSS-based memory limiting without spawning a shell.
- Add open3_safe gem dependency
- Propagate max_rss: keyword arg through public API and all extractors
- Convert env strings to Hashes for Open3 compatibility
- Remove 2>&1 redirects (stderr now captured separately)
- Duplicate/blank-line filtering preserved in the new implementation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+
## Commands
6
+
7
+
```bash
8
+
# Install dependencies (open3_safe is fetched from GitHub at a pinned commit ref — see Gemfile)
9
+
bundle install
10
+
11
+
# Run all tests
12
+
bundle exec rake test
13
+
14
+
# Run a single test file
15
+
bundle exec ruby test/unit/test_extract_text.rb
16
+
17
+
# Build and install the gem locally
18
+
bundle exec rake gem:install
19
+
```
20
+
21
+
## External Dependencies
22
+
23
+
The following system tools must be installed for full functionality:
24
+
-`gm` (GraphicsMagick) — image extraction and OCR pre-processing
25
+
-`pdftotext`, `pdfinfo`, `pdftk` — text extraction and PDF metadata
26
+
-`tesseract` — OCR (with optional `osd` language pack for orientation detection)
27
+
-`java` + JODConverter (vendored in `vendor/`) — non-PDF document conversion
28
+
-`open3_safe` gem — pinned to a specific GitHub commit ref in `Gemfile`; `Gemfile.lock` must be regenerated after changing the ref
29
+
30
+
## Architecture
31
+
32
+
`lib/docsplit.rb` is the public API entry point. It defines the `Docsplit` module, checks `PATH` for dependencies at load time, and delegates to extractor classes.
33
+
34
+
**Extractor classes** (`lib/docsplit/`):
35
+
-`TextExtractor` — extracts text via `pdftotext`, falls back to Tesseract OCR for pages below `MIN_TEXT_PER_PAGE` (100 bytes)
36
+
-`ImageExtractor` — rasterizes PDF pages via GraphicsMagick (`gm convert`/`gm mogrify`)
37
+
-`PdfExtractor` — converts non-PDF documents to PDF using LibreOffice or JODConverter (Java)
38
+
-`InfoExtractor` — parses `pdfinfo` output for metadata
39
+
-`PageExtractor` — bursts PDFs into single-page PDFs via `pdftk`/`pdftailor`
40
+
41
+
**`ExternalProcess` module** (`external_process.rb`) is mixed into extractor classes. Its `run` method wraps `Open3Safe.capture3_safe` to execute subprocesses with:
42
+
- timeout (SIGTERM → SIGKILL after 5s)
43
+
- optional RSS memory limit via `max_rss:`
44
+
- stdout+stderr merged, blank lines and consecutive duplicate lines filtered (guards against memory bloat from corrupt PDFs — silverfin/issues/1998)
45
+
46
+
**Timeout-aware public API**: `extract_text_with_timeouts` and `extract_images_with_timeouts` accept `timeout` (overall) and `item_timeout` (per page/file); `extract_pdf_with_timeout` accepts only `timeout`. RSS caps are not part of the public API — each extractor hardcodes its own `MAX_RSS` constant (`TextExtractor`/`ImageExtractor`: 512 MiB, `TextExtractor::TESSERACT_MAX_RSS`: 1 GiB, `PdfExtractor`: 2 GiB) and passes it into `run(..., max_rss:)` internally. The plain `extract_*` variants have no timeouts.
47
+
48
+
## Test Structure
49
+
50
+
Tests live in `test/unit/`, use Minitest, and write output to `test/output/` (cleaned up in `teardown`). Fixtures are in `test/fixtures/` — a mix of PDFs, Office docs, and edge-case files (encrypted, unicode, spaces/quotes in filenames).
# We're adding `| grep -v '^$' | uniq` here and below because if a corrupt PDF is parsed, it generates an infinite amount of identical warnings (with blank lines in between).
51
-
# By filtering these we avoid memory bloat when the executing process tries to capture stdout.
52
-
# See https://github.com/GetSilverfin/silverfin/issues/1998
0 commit comments