diff --git a/ppl2psd.py b/ppl2psd.py index d2fdf3a..1a269e9 100644 --- a/ppl2psd.py +++ b/ppl2psd.py @@ -527,9 +527,17 @@ def build_type_layer_tysh(ly: dict, font_ps: str) -> bytes: res["FontSet"][0]["Name"] = String(font_ps) - l, t, rr, bb = bounding_rect(ly) + l, t, rr, bb2 = bounding_rect(ly) tysh.transform = [1.0, 0.0, 0.0, 1.0, float(l), float(t)] - tysh.left, tysh.top, tysh.right, tysh.bottom = int(l), int(t), int(rr), int(bb) + tysh.left, tysh.top, tysh.right, tysh.bottom = int(l), int(t), int(rr), int(bb2) + # boundingBox is relative to the transform origin; PS shows the text at + # transform(boundingBox). Set it to (0,0)-(w,h) so the text lands on the + # ppl boundingRect (verified: l.psd display pos = transform x boundingBox). + bbd = td[b"boundingBox"] + bbd[b"Left"].value = 0.0 + bbd[b"Top "].value = 0.0 + bbd[b"Rght"].value = float(rr - l) + bbd[b"Btom"].value = float(bb2 - t) return tysh.tobytes() @@ -725,6 +733,23 @@ def convert(ppl_path: str, out_path: str, preview_path: Optional[str] = None, group_ranges[gid] = [i, i] else: group_ranges[gid][1] = i + # The app renders group children offset by the group bbox movement + # (moving a group updates the group bbox but not the child layer + # bboxes). Apply that offset so children land where the app shows them. + group_offset = {} + for gid, (first, last) in group_ranges.items(): + g = group_map.get(gid) + if not g or "boundingRect" not in g: + continue + gl, gt, _, _ = bounding_rect(g) + # only this group's OWN layers (nested groups' layers excluded) + own = [i for i in range(first, last + 1) + if layers[i].get("groupId") == gid] + if not own: + continue + inner_l = min(bounding_rect(layers[i])[0] for i in own) + inner_t = min(bounding_rect(layers[i])[1] for i in own) + group_offset[gid] = (gl - inner_l, gt - inner_t) rendered = [] skipped = 0 @@ -763,6 +788,9 @@ def convert(ppl_path: str, out_path: str, preview_path: Optional[str] = None, skipped += 1 continue img, (lx, ly_) = res + if gid and gid in group_offset: + lx = int(lx + group_offset[gid][0]) + ly_ = int(ly_ + group_offset[gid][1]) visible = ly.get("visible", "1") != "0" opacity = int(ly.get("alpha", "255") or 255) name = f"{ly['_type']}_{ly.get('objectId', i)}"