Is there any way to determine the anchor point when pasting the file?

In this case, I need to paste a group into my active Photoshop document. I open a file that has the layers I need through a picker, take the content and paste it into my active document.

This is where the issue comes in, the content is always pasted in the center of the document, defined as X = 0 | Y = 0 from the center of the file. For better location I would need the point X = 0 | Y = 0 was referenced in the top left corner of the document, so I could better move the assets through document.

Is there any alternative for this?

This is my code:


import React, { useState } from 'react';
const { core, app } = require('photoshop');
const { storage } = require('uxp');
const { batchPlay } = require('photoshop').action; // Importe a função batchPlay

export default function HeaderSelector() {
    const [selectedHeader, setSelectedHeader] = useState(null);

    const handleHeaderSelect = async (header) => {
        console.log('Header selecionado:', header);

        // Caminho relativo para o arquivo de cabeçalho específico dentro do diretório do plugin
        const headerFilePath = `assets/${header}.psd`;
        console.log('Caminho do arquivo:', headerFilePath);

        const fs = storage.localFileSystem;

        try {
            // Obtém a entrada do arquivo de cabeçalho
            const pluginDir = await fs.getPluginFolder();
            const fileEntry = await pluginDir.getEntry(headerFilePath);

            // Função alvo para execução dentro do modal
            const targetFunction = async (executionContext) => {
                try {
                    // Abre o arquivo PSD e executa operações usando batchPlay
                    await app.open(fileEntry);
                    const batchHeaderCopy = [
                        {
                            _obj: "selectAllLayers",
                            _target: [
                                {
                                    _ref: "layer",
                                    _enum: "ordinal",
                                    _value: "targetEnum"
                                }
                            ],
                            _options: {
                                dialogOptions: "dontDisplay"
                            }
                        },
                        {
                            _obj: "copyEvent",
                            _options: {
                                dialogOptions: "dontDisplay"
                            }
                        }
                    ];

                    await batchPlay(batchHeaderCopy, {}); // Use batchPlay desta forma
                    await app.activeDocument.save();
                    await app.activeDocument.close();

                    // Obtém o documento ativo no Photoshop
                    const activeDocument = app.activeDocument;

                    // Cola o conteúdo copiado para o documento ativo
                    await activeDocument.paste();

                    // Obtém a camada colada
                    const pastedGroup = activeDocument.layers[activeDocument.layers.length - 1];

                    // Calcula as coordenadas de deslocamento para o canto superior esquerdo
                    const offsetX = -pastedGroup.bounds.left - activeDocument.width / 5.5;
                    const offsetY = -pastedGroup.bounds.top - activeDocument.height / 5;

                    // Move o grupo colado para o ponto (0, 0) do documento (canto superior esquerdo)
                    pastedGroup.translate(offsetX, offsetY);

                    // Salva o documento modificado
                    await activeDocument.save();

                    console.log('Cabeçalho inserido com sucesso!');
                } catch (error) {
                    console.error('Erro ao inserir o cabeçalho:', error);
                }
            };

            // Configura as opções para o executeAsModal
            const options = {
                commandName: 'Inserir Cabeçalho', // Nome do comando para a barra de progresso
                interactive: true, // Modo interativo para a barra de progresso
            };

            // Executa targetFunction dentro do modal
            await core.executeAsModal(targetFunction, options);
        } catch (error) {
            console.error('Erro ao encontrar o arquivo:', error);
        }
    };

    return (
        <>
            <sp-picker>
                <sp-menu-item onClick={() => handleHeaderSelect('SB')}>SB</sp-menu-item>
                <sp-menu-item onClick={() => handleHeaderSelect('CON')}>CON</sp-menu-item>
                <sp-menu-divider></sp-menu-divider>
                <sp-menu-item onClick={() => handleHeaderSelect('Alienware')}>Alienware</sp-menu-item>
                <sp-menu-item onClick={() => handleHeaderSelect('Gaming')}>Gaming</sp-menu-item>
                <sp-menu-divider></sp-menu-divider>
                <sp-menu-item onClick={() => handleHeaderSelect('Outlet')}>Outlet</sp-menu-item>
                <sp-menu-item onClick={() => handleHeaderSelect('Experts')}>Experts</sp-menu-item>
            </sp-picker>
            {selectedHeader && <p>Selecionado: {selectedHeader}</p>}
        </>
    );
}

I partially solved my problem. However, I’m still having trouble getting the measurement in Pixels of my pastedGroup. I’ve already tried using the layers.bounds method, but it doesn’t seem to work very well with groups, not even layerSets.bounds.

I will need these measurements to define where the next added module will have to be based on the Header modules.

This is my code now, with the temporary method to position the layers in the desired location:

import React, { useState } from 'react';
const { core, app } = require('photoshop');
const { storage } = require('uxp');
const { batchPlay } = require('photoshop').action; // Importe a função batchPlay

export default function HeaderSelector() {
    const [selectedHeader, setSelectedHeader] = useState(null);

    const handleHeaderSelect = async (header) => {
        console.log('Header selecionado:', header);

        // Caminho relativo para o arquivo de cabeçalho específico dentro do diretório do plugin
        const headerFilePath = `assets/${header}.psd`;
        console.log('Caminho do arquivo:', headerFilePath);

        const fs = storage.localFileSystem;

        try {
            // Obtém a entrada do arquivo de cabeçalho
            const pluginDir = await fs.getPluginFolder();
            const fileEntry = await pluginDir.getEntry(headerFilePath);

            // Função alvo para execução dentro do modal
            const targetFunction = async (executionContext) => {
                try {
                    // Abre o arquivo PSD e executa operações usando batchPlay
                    await app.open(fileEntry);
                    const batchHeaderCopy = [
                        {
                            _obj: "selectAllLayers",
                            _target: [
                                {
                                    _ref: "layer",
                                    _enum: "ordinal",
                                    _value: "targetEnum"
                                }
                            ],
                            _options: {
                                dialogOptions: "dontDisplay"
                            }
                        },
                        {
                            _obj: "copyEvent",
                            _options: {
                                dialogOptions: "dontDisplay"
                            }
                        }
                    ];

                    await batchPlay(batchHeaderCopy, {}); // Use batchPlay desta forma
                    await app.activeDocument.save();
                    await app.activeDocument.close();

                    // Obtém o documento ativo no Photoshop
                    const activeDocument = app.activeDocument;

                    // Cola o conteúdo copiado para o documento ativo
                    await activeDocument.paste();

                    // Obtém a camada colada
                    const pastedGroup = activeDocument.layers[activeDocument.layers.length - 1];


                    // Obtém as dimensões do documento
                    const docWidth = activeDocument.width;
                    const docHeight = activeDocument.height;

                    console.log("Altura do Documento:", docHeight);
                    console.log("Largura do Documento:", docWidth);

                    // Calcula as coordenadas de deslocamento para o canto superior esquerdo do documento
                    const offsetX = ((docWidth - docWidth) - (docWidth / 2) + 330) ;
                    const offsetY = ((docHeight - docHeight) - (docHeight / 2) + 57) ;


                    // Move o grupo colado para o ponto (offsetX, offsetY) do documento (canto superior esquerdo)
                    pastedGroup.translate(offsetX, offsetY);



                    // Salva o documento modificado
                    await activeDocument.save();

                    console.log('Cabeçalho inserido com sucesso!');
                } catch (error) {
                    console.error('Erro ao inserir o cabeçalho:', error);
                }
            };

            // Configura as opções para o executeAsModal
            const options = {
                commandName: 'Inserir Cabeçalho', // Nome do comando para a barra de progresso
                interactive: true, // Modo interativo para a barra de progresso
            };

            // Executa targetFunction dentro do modal
            await core.executeAsModal(targetFunction, options);
        } catch (error) {
            console.error('Erro ao encontrar o arquivo:', error);
        }
    };

    return (
        <>
            <Theme theme="dark" scale="medium" color="dark">
                <h4>Selecione o Header meu broder</h4>
                <sp-field-group style={{ width: "100vw", display: "flex", flexDirection: "row", gap: "5px" }}>
                    <sp-picker style={{ width: "45vw", padding: "0 5px" }} id="picker-m" size="m" label="Selection type">
                        <sp-menu-item onClick={() => handleHeaderSelect('SB')}>SB</sp-menu-item>
                        <sp-menu-item onClick={() => handleHeaderSelect('CON')}>CON</sp-menu-item>
                        <sp-menu-divider></sp-menu-divider>
                        <sp-menu-item onClick={() => handleHeaderSelect('Alienware')}>Alienware</sp-menu-item>
                        <sp-menu-item onClick={() => handleHeaderSelect('Gaming')}>Gaming</sp-menu-item>
                        <sp-menu-divider></sp-menu-divider>
                        <sp-menu-item onClick={() => handleHeaderSelect('Outlet')}>Outlet</sp-menu-item>
                        <sp-menu-item onClick={() => handleHeaderSelect('Experts')}>Experts</sp-menu-item>
                    </sp-picker>
                </sp-field-group>
            </Theme>
            {selectedHeader && <p>Selecionado: {selectedHeader}</p>}
        </>
    );
}

Ps: When I use layer.bounds or layerSets in pasteGroup it returns the total size of the document. The same size of docWidht and docHeight consts.

Do you need to take transparency into account or not? I mean do you want visible pixel be at [0,0] or if placed image has transparent padding should it preserve?

1 Like

It doesn’t really matter if the pixels are visible or not. All groups have limiting containers.

So far, positioning the elements has been great, but as I can’t get the correct layer sizes, it’s difficult to make the functions automatic for positioning. I’m having to put specific values ​​for the groups positioning.

If that is not a concern you could group it into 1 layer group, select only 1 layer group and then via batchPlay use the layer alignment function and align layer group related to the canvas.

Or in batchPlay there is option to move selected layers to specific x,y coordinates but this cannot be recorded.

You could also merge/rasterize layers and then get its bounds easily and revert it… but depends what kind of bounds… should it include masks and layer effects?

To tag on with another option: You could consider turning everything you want to copy into a single smart object. Copy that over into your new document. It will drop in the middle but all the relative transforms within the object are frozen. It’s straight forward to query the smart object bounds via batchPlay. You can then position your smart object where you want it and convert it back to layers (e.g. recover its individual layers), which will be in place.