RepoPilotOpen in app →

layervault/psd.rb

Parse Photoshop files in Ruby with ease

Mixed

Stale — last commit 5y ago

worst of 4 axes
Use as dependencyMixed

last commit was 5y ago; top contributor handles 90% of recent commits

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • 6 active contributors
  • MIT licensed
  • CI configured
Show 3 more →
  • Tests present
  • Stale — last commit 5y ago
  • Single-maintainer risk — top contributor 90% of recent commits
What would change the summary?
  • Use as dependency MixedHealthy if: 1 commit in the last 365 days

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/layervault/psd.rb?axis=fork)](https://repopilot.app/r/layervault/psd.rb)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/layervault/psd.rb on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: layervault/psd.rb

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/layervault/psd.rb shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Stale — last commit 5y ago

  • 6 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Stale — last commit 5y ago
  • ⚠ Single-maintainer risk — top contributor 90% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live layervault/psd.rb repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/layervault/psd.rb.

What it runs against: a local clone of layervault/psd.rb — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in layervault/psd.rb | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 1977 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>layervault/psd.rb</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of layervault/psd.rb. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/layervault/psd.rb.git
#   cd psd.rb
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of layervault/psd.rb and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "layervault/psd.rb(\\.git)?\\b" \\
  && ok "origin remote is layervault/psd.rb" \\
  || miss "origin remote is not layervault/psd.rb (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "lib/psd.rb" \\
  && ok "lib/psd.rb" \\
  || miss "missing critical file: lib/psd.rb"
test -f "lib/psd/file.rb" \\
  && ok "lib/psd/file.rb" \\
  || miss "missing critical file: lib/psd/file.rb"
test -f "lib/psd/layer.rb" \\
  && ok "lib/psd/layer.rb" \\
  || miss "missing critical file: lib/psd/layer.rb"
test -f "lib/psd/node.rb" \\
  && ok "lib/psd/node.rb" \\
  || miss "missing critical file: lib/psd/node.rb"
test -f "lib/psd/layer_info.rb" \\
  && ok "lib/psd/layer_info.rb" \\
  || miss "missing critical file: lib/psd/layer_info.rb"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 1977 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~1947d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/layervault/psd.rb"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

psd.rb is a pure-Ruby parser for Adobe Photoshop (.psd) files that deserializes the binary PSD format into an in-memory tree structure. It extracts document metadata (dimensions, color mode, bit-depth), layer hierarchies with properties (name, visibility, opacity, blend modes), text data with font information, vector masks, layer comps, and can export flattened or per-layer images as PNG. It's the only comprehensive open-source PSD parser for Ruby. Single-gem architecture: lib/psd/ is the core parser with modular concerns—lib/psd/header.rb reads metadata, lib/psd/layer.rb handles layer trees, lib/psd/image_formats/ (raw/rle) and lib/psd/image_modes/ (rgb/cmyk/greyscale) handle decompression. lib/psd/image_exports/png.rb exports rendered images. examples/ contains ~10 runnable scripts demonstrating each major feature against sample .psd files.

👥Who it's for

Ruby developers building automation tools, asset pipelines, or design-system tooling that need to programmatically inspect Photoshop documents without opening Photoshop—typically designers integrating PSD exports into CI/CD, or teams extracting text/metadata from design files at scale.

🌱Maturity & risk

Actively maintained but moderately aged: the repo shows commits and a Travis CI pipeline (testing against MRI 1.9.3, 2.0.0, 2.1.0), but Ruby versions indicate last major work was ~2013. No GitHub stars visible in provided data, and the codebase is small (~181KB). Likely production-ready for stable use cases but not under active feature development.

Standard open source risks apply.

Active areas of work

Based on file structure alone, no active PRs or recent changes visible. The repo appears in steady-state: test suite and examples exist, but no hints of ongoing refactors or feature branches. Travis CI is configured but build status unknown from data provided.

🚀Get running

git clone https://github.com/layervault/psd.rb.git && cd psd.rb && bundle install && bundle exec rspec

Daily commands: bundle exec rspec runs tests. bundle exec ruby examples/parse.rb examples/images/example.psd will parse and print a sample file. Guardfile suggests bundle exec guard for watch-mode testing during development.

🗺️Map of the codebase

  • lib/psd.rb — Main entry point and public API for the library; all PSD parsing begins here
  • lib/psd/file.rb — Core file parser that orchestrates reading PSD binary structure including header, layers, and image data
  • lib/psd/layer.rb — Primary Layer abstraction representing document structure; handles hierarchy, visibility, and metadata
  • lib/psd/node.rb — Tree node structure that enables hierarchical layer navigation and composition
  • lib/psd/layer_info.rb — Dispatcher for parsing 40+ layer info blocks; critical for extracting effects, masks, and adjustments
  • lib/psd/header.rb — Parses PSD file header to determine color mode, dimensions, and bit depth
  • lib/psd/descriptor.rb — Parses Adobe descriptor format used throughout PSD for structured data (effects, gradients, etc.)

🛠️How to make changes

Add support for a new layer info block type

  1. Create new class in lib/psd/layer/info/ inheriting from Base or Descriptor, implementing self.should_parse? and parse() (lib/psd/layer/info/new_info_type.rb)
  2. Register the new info type by adding its class to the LAYER_INFO hash in lib/psd/layer/info.rb with its four-character key (lib/psd/layer/info.rb)
  3. Add getter method to lib/psd/layer.rb to expose parsed data (e.g., def my_info_field; @info_by_key[:MYKY]; end) (lib/psd/layer.rb)
  4. Write spec covering parsing of real PSD files containing this layer info type (spec/psd/layer/info/new_info_type_spec.rb)

Add a new image color mode

  1. Create color mode handler in lib/psd/image_modes/ implementing to_rgb!() and merge() for channel combination (lib/psd/image_modes/new_mode.rb)
  2. Update lib/psd/image.rb to instantiate and route to the new mode based on file header color_mode value (lib/psd/image.rb)
  3. Add test PSD file with the new color mode to examples/images/ and verify flattening produces correct output (examples/images/example-newmode.psd)

Add a new layer export format

  1. Create exporter class in lib/psd/image_exports/ implementing export() method to serialize pixel data (lib/psd/image_exports/new_format.rb)
  2. Add public method to lib/psd/layer/exporting.rb calling the exporter (e.g., def to_new_format(); ImageExports::NewFormat.new(...).export(); end) (lib/psd/layer/exporting.rb)
  3. Write integration test in examples/ demonstrating export of layer or document to the new format (examples/export_new_format.rb)

🔧Why these technologies

  • Pure Ruby binary parsing — Enables cross-platform PSD parsing without native dependencies; trades speed for portability and maintainability
  • Lazy evaluation (LazyExecute) — Defers expensive image decompression and flattening until explicitly requested to minimize memory and startup latency
  • Tree node structure (Node class) — Mirrors PSD's hierarchical layer/folder nesting, enabling intuitive DOM-like traversal and manipulation
  • Descriptor format parser — Adobe's descriptor is used throughout PSD for effects, vector data, and adjustments; single unified parser avoids duplication
  • Ruby-vips/ImageMagick integration — Outsources PNG encoding and color space conversion to battle-tested libraries rather than reimplementing image codecs

⚖️Trade-offs already made

  • Pure Ruby implementation over native C extension

    • Why: Maximize compatibility and reduce deployment friction
    • Consequence: Image parsing is slower than native code; psd_native gem available for performance-critical paths
  • Lazy evaluation of image data

    • Why: Avoid loading and decompressing gigabytes of pixel data for metadata-only use cases
    • Consequence: Image flattening is deferred, so caller must explicitly invoke to_image/to_png to materialize pixels
  • Support all 40+ layer info block types with individual classes

    • Why: Each info type has unique parsing logic (effects use descriptors, text uses external enginedata, vectors use path arrays)
    • Consequence: Large codebase with many small specialized parsers; non-implemented blocks fail gracefully
  • Parse but not write PSD files

    • Why: Reading is vastly more common; writing requires maintaining binary format parity with Adobe
    • Consequence: Library is read-only; modifications require external tools

🚫Non-goals (don't propose these)

  • Writing or modifying PSD files (read-only parser)
  • Cross-platform real-time preview rendering (outputs flattened pixels, not GPU-accelerated preview)
  • Support for undocumented or vendor-specific layer types beyond Adobe's published spec
  • Pixel-perfect color accuracy across all color spaces without calibration
  • Streaming or out-of-core parsing (entire file structure loaded into memory)

🪤Traps & gotchas

Binary format is Photoshop-version-sensitive—older or newer PSD files may have unimplemented resource blocks (see examples/unimplemented_info.rb), causing silent skips. No explicit error on unknown layer blend modes; ensure blend_mode.rb is exhaustive. Image export requires pixels to be decompressed and composited in correct order; layer visibility and opacity are honored in export but blend mode blending is not (flat composite only). Descriptor parsing is recursive and can be deeply nested; stack depth limits possible. No documented handling of non-ASCII layer/text names; encoding may need explicit UTF-8 handling depending on PSD source.

🏗️Architecture

💡Concepts to learn

  • PSD Binary Format Sections — Understanding the three-part PSD structure (Header → Image Resources → Layer Records → Image Data) is essential to understand lib/psd/file.rb's seek patterns and why descriptor parsing is recursive.
  • RLE (Run-Length Encoding) — Photoshop's primary pixel compression; lib/psd/image_formats/rle.rb decompresses this losslessly. Without understanding RLE, pixel-level features are opaque.
  • Descriptor Format (Photoshop proprietary) — Nested key-value structure used for text, effects, vector masks, and metadata; lib/psd/descriptor.rb recursively parses it. Critical for extracting anything beyond layer names.
  • Tree Traversal Patterns — Photoshop folders are represented as tree nodes with children; lib/psd/layer.rb implements ancestor, sibling, descendant methods. Essential for traversing layer hierarchies.
  • Blend Modes (Photoshop) — Layers have blend modes (multiply, screen, overlay, etc.) that affect compositing; lib/psd/blend_mode.rb maps names to modes. Must understand for proper image export.
  • Color Space Conversions (RGB, CMYK, Greyscale) — PSDs store pixels in different color modes; lib/psd/image_modes/ contains mode-specific decoders. Critical for correct pixel interpretation in export.
  • Binary File I/O with Bit-Level Positioning — lib/psd/file.rb uses seek() and read() to navigate arbitrary byte offsets in the PSD; understanding little-endian integers and struct packing is mandatory.
  • layervault/psd-enginedata — Sibling gem that parses Photoshop text engine data (font names, sizes, colors); required dependency for extracting rich text from PSD files
  • layervault/psd_native — Native C extension companion that accelerates psd.rb parsing ~5–10x by reimplementing hot paths; optional but recommended for production pipelines
  • wycats/thor — CLI framework used implicitly in examples; not a direct dependency but commonly paired with psd.rb for command-line asset extraction tools
  • jnunemaker/chunky_png — Pure-Ruby PNG encoder dependency (inferred); psd.rb uses it to write layer and composite images without external binaries
  • adobe/psd-html-exporter — JavaScript-based PSD parser that shares the same Adobe PSD format spec; useful for comparing parsing logic across ecosystems

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive test coverage for lib/psd/layer/info/* adjustment layer classes

The repo has 20+ adjustment layer info classes (black_white.rb, curves.rb, hue_saturation.rb, etc.) but based on the file structure, there's no visible dedicated test suite for these parsers. Each adjustment layer type has unique binary format requirements. Adding unit tests for parsing, serialization, and edge cases would prevent regressions and document expected behavior.

  • [ ] Create spec/lib/psd/layer/info/ directory matching source structure
  • [ ] Add unit tests for each info class (start with curves.rb, hue_saturation.rb, color_balance.rb as high-value targets)
  • [ ] Include tests for: descriptor parsing, property validation, and round-trip serialization where applicable
  • [ ] Reference examples/export_text_data.rb pattern to create test fixtures for adjustment layers

Add support for additional image export formats beyond PNG (lib/psd/image_exports/)

Currently only lib/psd/image_exports/png.rb exists, but users frequently need JPEG and WebP exports for web use cases. The image_modes (RGB, CMYK, Greyscale) and image_formats infrastructure is already in place. Adding JPEG and WebP exporters would be high-value and build on existing architecture.

  • [ ] Create lib/psd/image_exports/jpeg.rb following the PNG exporter pattern
  • [ ] Create lib/psd/image_exports/webp.rb with quality/compression options
  • [ ] Add integration tests in spec/ using examples/images/ PSD files (comp-example.psd, example-cmyk.psd)
  • [ ] Update README with export format examples similar to examples/export_image.rb

Implement vector mask data extraction for lib/psd/layer/

The README explicitly lists 'Vector mask data' as a supported feature, and the file structure shows vector/path handling (examples/path.rb exists), but there's no visible lib/psd/layer/info/ class for vector masks. This is a concrete gap between documented capabilities and implementation.

  • [ ] Create lib/psd/layer/info/vector_mask.rb to parse vector mask additional layer info
  • [ ] Create lib/psd/layer/vector_path.rb or extend existing path.rb to represent mask paths
  • [ ] Add parsing logic for path records and knot data following PSD spec vector mask format
  • [ ] Create example examples/vector_masks.rb demonstrating extraction and geometry access
  • [ ] Add unit tests in spec/lib/psd/layer/info/vector_mask_spec.rb using examples/images/ files

🌿Good first issues

  • Add RSpec tests for lib/psd/color.rb (referenced but no test file shown); verify CMYK↔RGB conversion against known Photoshop outputs.
  • Document and test lib/psd/blend_mode.rb coverage against full Photoshop blend mode list; add missing modes and ensure example files use them.
  • Extend examples/export_layer_images.rb to handle layer effects (drop shadow, stroke, etc.) by checking for layer/effects descriptor blocks and documenting which effects are parsed vs. ignored.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • d8c1136 — Merge pull request #104 from LoginovIlya/fix_read_string (meltingice)
  • 6271072 — Fixed read_string bug (LoginovIlya)
  • 6f6359b — Version 3.9.0 (meltingice)
  • 4d18b1a — Add support for parsing the work path (meltingice)
  • ef92e11 — Merge branch 'master' of github.com:layervault/psd.rb (meltingice)
  • 79e0625 — Change name of paths to saved_paths (meltingice)
  • dab928c — Add support for parsing saved paths (meltingice)
  • 31c7ddd — Merge pull request #93 from redcap97/resolution_info (meltingice)
  • f234001 — @h_res and @v_res aren't signed numbers (redcap97)
  • 7036d09 — added support for ResolutionInfo (redcap97)

🔒Security observations

PSD.rb is a specialized file parser library with moderate security posture. The primary security concerns relate to input validation during binary file parsing (potential DoS/resource exhaustion) and lack of output sanitization guarantees for exported layer data. The library itself does not handle web requests or databases, limiting injection attack vectors. However, applications integrating this library should implement proper input/output validation. No hardcoded secrets or critical misconfigurations were detected. Dependency audit information was unavailable but should be performed separately. The codebase appears well-structured but would benefit from explicit security guidelines for handling untrusted PSD files.

  • Medium · Potential Arbitrary File Parsing Risk — lib/psd/file.rb, lib/psd/layer/info/, lib/psd/descriptor.rb. The PSD.rb library parses binary Photoshop files without apparent input validation or schema enforcement. Malformed or crafted PSD files could potentially cause excessive memory consumption, denial of service, or trigger unexpected behavior during parsing of layer information, image data, or descriptors. Fix: Implement input validation for PSD file headers and structure. Add file size limits, memory limits during parsing, and validate binary data structure integrity before processing. Consider sandboxing file parsing operations.
  • Medium · No Apparent Output Sanitization in Export Functions — lib/psd/image_exports/, lib/psd/layer/exporting.rb, examples/export_text_data.rb. The library exports layer data and images (PNG export in lib/psd/image_exports/png.rb and text data extraction). If exported data is subsequently used in web contexts without sanitization, it could pose XSS risks. Layer names and text content from PSD files are directly extracted without sanitization. Fix: When exposing PSD data (especially text content and layer names) through APIs or web interfaces, implement proper output encoding/escaping. Document that consumers must sanitize this data before use in HTML contexts.
  • Low · Dependency Management Not Visible — Gemfile, *.gemspec. The Gemfile and dependency specifications were not provided in the analysis context. Unable to verify if the library or its dependencies have known vulnerabilities or outdated versions. Fix: Review Gemfile for dependencies and ensure all gems are kept up-to-date. Use bundle audit regularly to check for known vulnerabilities in dependencies. Pin specific versions of critical dependencies.
  • Low · No HTTPS Enforcement in Documentation — README.md. README references external dependencies (psd_native, psd-enginedata) via GitHub links without explicit HTTPS protocol verification mentioned. Fix: Ensure all external resource links use HTTPS. Verify integrity of external dependencies through checksum verification or signed releases.
  • Low · Test/Example Files May Contain Sensitive Data — examples/images/. The repository includes example PSD files (examples/images/*.psd) which could potentially contain sensitive information if committed from real-world documents. Fix: Audit example PSD files to ensure they don't contain sensitive information. Consider using minimal/sanitized example files. Add PSD files to .gitignore if they're generated during testing.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.