""" Generator PSD — Podziękowanie dla gości buteleczka urodziny (Wzór 3). Otwiera szablon PSD, podmienia teksty w Smart Object z zachowaniem pozycji warstw, zapisuje jako nowy PSD w folderze _gotowe. Szablon zawiera 8 linked instancji tego samego Smart Object (arkusz drukarski) — edycja jednego SO propaguje się do wszystkich 8 kopii. Wymaga: uruchomiony Adobe Photoshop, pakiet photoshop-python-api. Użycie: python buteleczki_urodziny_wzor3.py --imie "Agnieszki" --wiek "25" --klient "Agnieszka Kowalska" python buteleczki_urodziny_wzor3.py --imie "Agnieszki" --wiek "25" \ --naglowek "urodziny" --klient "Agnieszka Kowalska" """ import argparse import os import photoshop.api as ps # --- Ścieżki --- PROJEKT_DIR = os.path.join( r"d:\pomysloweprezenty.pl\projekty\urodziny - buteleczki", "Podziękowanie dla gości buteleczka z nadrukiem UV - Wzór 3", ) SZABLON_PATH = os.path.join(PROJEKT_DIR, "szablon 370x300.psd") GOTOWE_DIR = os.path.join(PROJEKT_DIR, "_gotowe") # Nazwa warstwy Smart Object w głównym PSD. # W szablonie jest 8 linked kopii tego samego SO ("Warstwa 2", "Warstwa 2 kopia", ...). # Wystarczy wejść w dowolną — zmiana propaguje się do wszystkich. SMART_OBJECT_LAYER = "Warstwa 2" # Grupa w Smart Object, w której leżą warstwy tekstowe. TEKST_GROUP = "Warstwa 1" def open_smart_object(app): """Otwiera zawartość aktywnej warstwy Smart Object do edycji.""" desc = ps.ActionDescriptor() ref = ps.ActionReference() ref.putEnumerated( app.stringIDToTypeID("layer"), app.stringIDToTypeID("ordinal"), app.stringIDToTypeID("targetEnum"), ) desc.putReference(app.stringIDToTypeID("null"), ref) app.executeAction(app.stringIDToTypeID("placedLayerEditContents"), desc) def change_text_preserve_position(layer, new_text): """Zmienia tekst warstwy z zachowaniem jej oryginalnej pozycji. Uwzglednia justowanie: - center (2): zachowuje srodek poziomy - right (3): zachowuje prawa krawedz - left (1): zachowuje lewa krawedz W pionie zawsze zachowuje gora (pierwsza linia bazowa). """ bounds_before = [float(b) for b in layer.bounds] try: just_int = int(layer.textItem.justification) except Exception: just_int = 1 layer.textItem.contents = new_text bounds_after = [float(b) for b in layer.bounds] if just_int == 2: cx_before = (bounds_before[0] + bounds_before[2]) / 2 cx_after = (bounds_after[0] + bounds_after[2]) / 2 dx = cx_before - cx_after elif just_int == 3: dx = bounds_before[2] - bounds_after[2] else: dx = bounds_before[0] - bounds_after[0] dy = bounds_before[1] - bounds_after[1] if dx != 0 or dy != 0: layer.translate(dx, dy) def generate(imie, wiek, klient, naglowek=None): """Generuje PSD z podmienionymi danymi.""" os.makedirs(GOTOWE_DIR, exist_ok=True) output_path = os.path.join(GOTOWE_DIR, f"{klient}.psd") app = ps.Application() doc = app.open(SZABLON_PATH) print(f"Otwarto szablon: {doc.name}") # Znajdź i otwórz Smart Object try: target = doc.artLayers[SMART_OBJECT_LAYER] except Exception: doc.close(ps.SaveOptions.DoNotSaveChanges) raise RuntimeError(f"Nie znaleziono warstwy '{SMART_OBJECT_LAYER}'") app.activeDocument.activeLayer = target open_smart_object(app) so_doc = app.activeDocument # Nawigacja do grupy z tekstami grupa = so_doc.layerSets[TEKST_GROUP] # Podmiana tekstów replacements = { "imie": imie, "wiek": str(wiek), } if naglowek is not None: replacements["naglowek"] = naglowek for layer_name, new_text in replacements.items(): layer = grupa.artLayers[layer_name] old_text = layer.textItem.contents change_text_preserve_position(layer, new_text) print(f" {layer_name}: \"{old_text}\" -> \"{new_text}\"") # Zapisz Smart Object (propaguje do wszystkich 8 linked instancji) so_doc.save() so_doc.close() print("Smart Object zapisany") # Zapisz jako nowy PSD psd_opts = ps.PhotoshopSaveOptions() app.activeDocument.saveAs(output_path, psd_opts, True) print(f"Zapisano: {output_path}") # Zamknij oryginał bez zapisu app.activeDocument.close(ps.SaveOptions.DoNotSaveChanges) print("Gotowe!") return output_path def main(): parser = argparse.ArgumentParser( description="Generator PSD - buteleczki urodziny Wzór 3" ) parser.add_argument("--imie", required=True, help="Imię w dopełniaczu (np. Agnieszki)") parser.add_argument("--wiek", required=True, help="Wiek jubilata (np. 25)") parser.add_argument("--klient", required=True, help="Imię i nazwisko klienta (nazwa pliku wyjściowego)") parser.add_argument("--naglowek", default=None, help="Tekst nagłówka (domyślnie: 'urodziny' z szablonu)") args = parser.parse_args() generate( imie=args.imie, wiek=args.wiek, klient=args.klient, naglowek=args.naglowek, ) if __name__ == "__main__": main()