Commit Graph
15 Commits
Author SHA1 Message Date
AskaEth 9ef308b469 Sync StyleRun/ParagraphRun run lengths with new text length
Photoshop warns the text layer needs conversion / editing changes layout
when the run length array (character-style/paragraph length per run) does
not match the text. We changed Editor.Text but left RunLengthArray at the
template's 16, so PS flagged every TypeLayer. Now set both run length
arrays to len(text).
2026-08-27 16:03:42 +08:00
AskaEth d322404e5a Fix punctuation glyph overlap and line-end clipping
PIL draws full-width punctuation offset inside the advance box (e.g.
left bracket '(' ink sits at x+42..x+61 while its advance is only 0.45em),
so the next glyph overlapped it and a trailing bracket could clip. Shift
the draw x by the ink offset for narrow punctuation and add right padding
so line ends never clip. Verified: bracket gap restored, line end intact.
2026-08-27 15:59:04 +08:00
AskaEth 4fcc5acc85 Only apply group offset when bbox is disjoint from content
The app only moves group children when the group bbox is disjoint from
its content (group moved away, e.g. groups 169/181). When the bbox
contains the content (e.g. group_64 with a taller frame), children stay
at their ppl coordinates. Fixes group_64 being shifted up 1225px.
2026-08-27 15:51:15 +08:00
AskaEth 3568472eeb Fix group offsets and TypeLayer boundingBox
- Group children render offset by the group bbox movement (the app moves
  groups without updating child layer bboxes). Apply the group offset
  using only the group's OWN layers (exclude nested groups' layers from
  the inner bbox, which wrongly shifted parent groups like group_167).
- TypeLayer boundingBox is relative to the transform origin; set it to
  (0,0)-(w,h) so text renders at the ppl boundingRect (verified:
  display pos = transform x boundingBox).
Fixes: overlapping groups 178/181, '最佳记录' shifted up, shape_168
dropped down, overall diff vs app render 11.84 -> 8.84.
2026-08-27 15:43:41 +08:00
AskaEth ea2d043d26 Add README and refine .gitignore
README documents both converters, features (raster/TypeLayer/group/bold),
installation, usage, limitations and calibration notes. .gitignore excludes
Python caches, sample assets and large media files while keeping the TySh
template.
2026-08-27 12:37:10 +08:00
AskaEth bcb3ad0c47 Map ppl layer groups to PSD groups via lsct section dividers
Groups ([group] sections + layer groupId) become PSD groups: a bounding
divider (lsct kind 3) opens the group at its bottom, an open-folder
divider (kind 1) closes it at the top (giving the group its name), per
psd_tools' parsing order. Group divider layers get empty channels.
Verified: psd_tools reads the group with its 11 child layers.
2026-08-27 11:01:58 +08:00
AskaEth 39c27504d4 Map textFontStyle=1 (faux bold) to PSD
- TypeLayer mode: StyleSheetData.FauxBold=true for bold text layers
- Rasterized mode: render with stroke width for faux bold
Verified: 50/54 text layers get FauxBold, stroke rendering thickens glyphs.
2026-08-27 10:49:57 +08:00
AskaEth c6a24a4fdb Add --type-layer: convert text layers to editable TypeLayers (TySh)
Reuses a reference TySh block from a PS-generated PSD as a template and
mutates its engine data (Text/FontSize/FillColor/FontSet/transform) per
ppl text layer. Rasterized pixels are kept as channel data so the layer
always displays, and Photoshop treats it as an editable text layer.
Font PostScript names are read from the font files via fontTools.
Verified: 54 TypeLayers, psd_tools reads correct text/engine data.
2026-08-27 10:31:36 +08:00
AskaEth edef8799ce Fix blending ranges: zero composite range made PS skip layer blending
PS reads layer blending ranges to decide if a layer participates in
compositing. psd_tools wrote all-zero ranges (composite 0..0), which tells
Photoshop the layer has an empty blend range -> the canvas renders empty
even though layer pixels are correct (Ctrl+C/V shows content, export PNG is
transparent, save-as by PS keeps the bad ranges). Now writes full-range
(0..65535) composite + channel ranges like PS-generated files, verified
against the reference PSD.
2026-08-27 10:02:17 +08:00
AskaEth 8af857d45a Fix luni block: missing 4-byte character count field
The unicode layer name block must be [char_count(4B)][utf-16-be chars].
We wrote raw utf-16 without the count, so Photoshop read the first
characters as the length, shifted the entire extra-data parse, and every
layer's pixels came out empty (all layers transparent). Fixed: name now
reads back correctly and 167/167 layers match the rendered originals
(mean diff 0.000).
2026-08-27 09:25:18 +08:00
AskaEth 00f2cbe71a Write standard tagged blocks (lnsr/lyid/clbl/infx/knko/lspf/lclr/fxrp) per layer
Photoshop reads a layer's tagged blocks until it sees a non-8BIM
signature. With only 'luni' the next bytes are the channel data, which PS
mis-parses and the layer pixels come out empty. PS writes the standard
block set, so we now mirror it (verified: blocks list now matches the
reference PSD).
2026-08-27 09:06:03 +08:00
AskaEth e07ef4acaa Set layer flags bit3 (Photoshop 5.0+) so PS reads tagged blocks correctly
PS-generated PSDs always set flags bit 3 (0x08) to signal Photoshop 5.0+
format where the layer extra data contains tagged blocks. Our layers had
flags=0x00, so Photoshop treated them as pre-5.0 records and failed to
read the extra data / layer pixels, showing every layer as transparent
(only ape_2 happened to render). Verified against the reference PSD.
2026-08-27 08:59:35 +08:00
AskaEth b76a26c11c Fix merged image data: single compression field + RAW for compatibility
Per Adobe PSD spec, the Image Data Section has ONE 2-byte compression
field followed by all planes in planar order (RRR GGG BBB). The previous
code wrote a compression marker per channel (4 extra bytes) and used RLE,
which third-party viewers parse incorrectly (stretching) and Photoshop
rejected. Now writes RAW data with a single compression marker, verified
by pytoshop (decompresses to 3x4882x9764).
2026-08-27 08:37:59 +08:00
AskaEth a5757d1f07 Fix PSD structure: add missing LayerInfo length field and correct channel order
- Layer info section was missing its 4-byte length field, causing all
  subsequent offsets to shift and Photoshop to fail with 'unexpected end
  of file'
- Layer channel data was ordered [R,G,B,A] with ids [-1,0,1,2], shifting
  every channel by one (alpha held red, red held green, ...) - fixed to
  [A,R,G,B] so id -1 is the alpha mask
- Verified: psd_tools opens the PSD, per-layer pixels match the rendered
  originals with 0.000 mean difference
2026-08-27 08:27:05 +08:00
AskaEth 60624f01c2 Add ppl<->psd conversion tools (ppl2psd, psd2ppl) 2026-08-27 08:16:44 +08:00