<?xml version="1.0" encoding="UTF-8" standalone="yes"?><oembed><version><![CDATA[1.0]]></version><provider_name><![CDATA[Ondrej Paska Blog]]></provider_name><provider_url><![CDATA[https://fishtrone.wordpress.com]]></provider_url><author_name><![CDATA[Ondrej Paska]]></author_name><author_url><![CDATA[https://fishtrone.wordpress.com/author/randalfien/]]></author_url><title><![CDATA[Generating images from code with&nbsp;Photopea]]></title><type><![CDATA[link]]></type><html><![CDATA[<p><a href="https://www.photopea.com/" target="_blank" rel="noopener noreferrer">Photopea </a>is a free Photoshop alternative that lives in the browser. If you haven&#8217;t used it already, you should definitely give it a try! It&#8217;s fast, requires no installation and will feel very comfortable to anyone who is used to Photoshop&#8217;s interface.</p>
<p>The part I want to focus on is the<a href="https://www.photopea.com/learn/scripts"> scripting it supports</a> &#8211; mainly how to make it generate a zip file with PNG images based on a template.</p>
<p><img loading="lazy" data-attachment-id="45" data-permalink="https://fishtrone.wordpress.com/2019/02/25/generating-images-from-code-with-photopea/photopea/" data-orig-file="https://fishtrone.files.wordpress.com/2019/02/photopea.png" data-orig-size="582,497" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="photopea" data-image-description="" data-image-caption="" data-medium-file="https://fishtrone.files.wordpress.com/2019/02/photopea.png?w=300" data-large-file="https://fishtrone.files.wordpress.com/2019/02/photopea.png?w=582" class="  wp-image-45 aligncenter" src="https://fishtrone.files.wordpress.com/2019/02/photopea.png?w=452&#038;h=386" alt="photopea" width="452" height="386" srcset="https://fishtrone.files.wordpress.com/2019/02/photopea.png?w=452&amp;h=386 452w, https://fishtrone.files.wordpress.com/2019/02/photopea.png?w=150&amp;h=128 150w, https://fishtrone.files.wordpress.com/2019/02/photopea.png?w=300&amp;h=256 300w, https://fishtrone.files.wordpress.com/2019/02/photopea.png 582w" sizes="(max-width: 452px) 100vw, 452px" /></p>
<p>As in Photoshop, scripts are written in Javascript using <a href="https://www.adobe.com/content/dam/acom/en/devnet/photoshop/pdfs/photoshop-cc-javascript-ref-2015.pdf" target="_blank" rel="noopener noreferrer">an API</a> for interacting with the elements in the document. <a href="https://www.adobe.com/content/dam/acom/en/devnet/photoshop/pdfs/photoshop-cc-javascript-ref-2015.pdf" target="_blank" rel="noopener noreferrer">The API</a> can be a bit puzzling, so here are some common functions:</p>
<pre>app.activeDocument.layerSets // get all folders in a document
app.activeDocument.artLayers // get all layers in a document (not in folders)
layer.visible = true/false // toggle layer visibility
layer.textItem.contents = "" // set text to a text layer
layer.translate(layer.bounds[0]-X, layer.bounds[1]-Y) // move layer to X, Y</pre>
<p>I started experimenting with this because for our upcoming board-game we have data for 32 cards in Google Sheets &#8211; name, strength, abilities etc. Generating prototypes for printing by hand was cumbersome and error-prone. So I decided to automate it. Similarly you might generate business cards, invitations, etc.</p>
<p>The solution I came up with takes data from Google Sheets in JSON form (scripting in Google Sheets is for another blog post). Based on the data, it shows/hides some layers and changes the text in text layers.</p>
<p>Here are some utility functions:<br />
<!-- HTML generated using hilite.me --></p>
<div style="background:#ffffff;overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;">
<pre style="margin:0;line-height:125%;"><span style="color:#008800;font-weight:bold;">function</span> getLayer(s){ <span style="color:#888888;">// finds layer by name</span>
 <span style="color:#008800;font-weight:bold;">var</span> layers = app.activeDocument.layers;
 <span style="color:#008800;font-weight:bold;">for</span>(<span style="color:#008800;font-weight:bold;">var</span> i = <span style="color:#0000dd;font-weight:bold;">0</span>; i &lt; layers.length; i++ ){
  <span style="color:#008800;font-weight:bold;">if</span>( layers[i].name == s ) <span style="color:#008800;font-weight:bold;">return</span> layers[i];
 }
 <span style="color:#008800;font-weight:bold;">return</span> <span style="color:#008800;font-weight:bold;">null</span>;
}

<span style="color:#008800;font-weight:bold;">function</span> getLayerSet(s){  <span style="color:#888888;">// finds folder by name</span>
 <span style="color:#008800;font-weight:bold;">var</span> layers = app.activeDocument.layerSets;
 <span style="color:#008800;font-weight:bold;">for</span>(<span style="color:#008800;font-weight:bold;">var</span> i = <span style="color:#0000dd;font-weight:bold;">0</span>; i &lt; layers.length; i++ ){
  <span style="color:#008800;font-weight:bold;">if</span>( layers[i].name == s ) <span style="color:#008800;font-weight:bold;">return</span> layers[i];
 }
 <span style="color:#008800;font-weight:bold;">return</span> <span style="color:#008800;font-weight:bold;">null</span>;
}

<span style="color:#008800;font-weight:bold;">function</span> activateOnly(s, set){ <span style="color:#888888;">// activates one layer in a folder</span>
 set = set || app.activeDocument;
 <span style="color:#008800;font-weight:bold;">var</span> layers = set.artLayers;
 <span style="color:#008800;font-weight:bold;">for</span>(<span style="color:#008800;font-weight:bold;">var</span> i = <span style="color:#0000dd;font-weight:bold;">0</span>; i &lt; layers.length; i++ ){
  layers[i].visible = layers[i].name == s;
 }
}
</pre>
</div>
<p>&nbsp;</p>
<p>For each data item, show the correct layers and text&#8230;<!-- HTML generated using hilite.me --></p>
<div style="background:#ffffff;overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;">
<pre style="margin:0;line-height:125%;">activateOnly( item.color +<span style="color:#dd2200;background-color:#fff0f0;">" border"</span>, getLayerSet(<span style="color:#dd2200;background-color:#fff0f0;">"border"</span>) );
  
<span style="color:#008800;font-weight:bold;">var</span> namelayer = getLayer(<span style="color:#dd2200;background-color:#fff0f0;">"Name Text"</span>);   
namelayer.textItem.contents = item.name;
</pre>
</div>
<p>&nbsp;</p>
<div data-shortcode="caption" id="attachment_46" style="width: 337px" class="wp-caption aligncenter"><img loading="lazy" aria-describedby="caption-attachment-46" data-attachment-id="46" data-permalink="https://fishtrone.wordpress.com/2019/02/25/generating-images-from-code-with-photopea/capture/" data-orig-file="https://fishtrone.files.wordpress.com/2019/02/capture.png" data-orig-size="327,339" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Capture" data-image-description="" data-image-caption="" data-medium-file="https://fishtrone.files.wordpress.com/2019/02/capture.png?w=289" data-large-file="https://fishtrone.files.wordpress.com/2019/02/capture.png?w=327" class=" size-full wp-image-46 aligncenter" src="https://fishtrone.files.wordpress.com/2019/02/capture.png?w=327&#038;h=339" alt="Capture" width="327" height="339" srcset="https://fishtrone.files.wordpress.com/2019/02/capture.png 327w, https://fishtrone.files.wordpress.com/2019/02/capture.png?w=145&amp;h=150 145w, https://fishtrone.files.wordpress.com/2019/02/capture.png?w=289&amp;h=300 289w" sizes="(max-width: 327px) 100vw, 327px" /><p id="caption-attachment-46" class="wp-caption-text">Example layer structure</p></div>
<p>&#8230; and export it as PNG.<!-- HTML generated using hilite.me --></p>
<div style="background:#ffffff;overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;">
<pre style="margin:0;line-height:125%;">    <span style="color:#008800;font-weight:bold;">var</span> opts = <span style="color:#008800;font-weight:bold;">new</span> ExportOptionsSaveForWeb();
    opts.format = SaveDocumentType.PNG;
    opts.PNG8 = <span style="color:#008800;font-weight:bold;">false</span>;
    opts.quality = <span style="color:#0000dd;font-weight:bold;">100</span>;
    
    pngFile = <span style="color:#008800;font-weight:bold;">new</span> File(item.folder+<span style="color:#dd2200;background-color:#fff0f0;">"/"</span>+item.name+<span style="color:#dd2200;background-color:#fff0f0;">".png"</span>);
    app.activeDocument.exportDocument(pngFile, ExportType.SAVEFORWEB, opts);</pre>
</div>
<p>&nbsp;</p>
<p>Saving this script in a jsx file, you can drag it to Photopea to run it.</p>
<p>After the script ends, it spits out a .zip file with all the exported images in a folder structure. On my machine it generates 32 high-res PNGs in about 12 seconds &#8211; much faster than Photoshop!</p>
<p>There are some limits on what you can do, for example I haven&#8217;t figured out how to change the color of only part of a text.</p>
<p>Hope you find this useful.</p>
]]></html><thumbnail_url><![CDATA[https://fishtrone.files.wordpress.com/2019/02/capture.png?fit=440%2C330]]></thumbnail_url><thumbnail_width><![CDATA[318]]></thumbnail_width><thumbnail_height><![CDATA[330]]></thumbnail_height></oembed>