CommunityImagemgithub.com

4hm4di/ahkab-illustrator-text

Use ao entregar layouts editáveis no Illustrator (revistas, perfis, brochuras) construídos como SVG/PDF, de modo que cada parágrafo se torne uma caixa de texto editável do tipo área, não linhas agrupadas.

O que é ahkab-illustrator-text?

Esta skill resolve o problema comum de o Illustrator importar texto de SVG/PDF dividindo-o em objetos de texto por linha, agrupados, o que inviabiliza edição fluida. Em vez disso, ela gera um script ExtendScript (.jsx) que, ao ser executado no Illustrator (File > Scripts > Other Script...), recria todo o texto nativamente como caixas de texto de área ou ponto, uma por parágrafo/título, preservando posição, tamanho, fonte, cor, entrelinhamento e espaçamento. O script também lida com importação de arte SVG (camadas nomeadas), criação de mesas de trabalho (artboards) para múltiplas páginas, e aplica estilos ricos com suporte a múltiplas fontes (fallback). É ideal para fluxos de design editorial (revistas, brochuras, perfis) onde o cliente precisa editar textos diretamente no .ai sem quebrar o layout. A skill recomenda testar o script gerado em um ambiente Node simulado (mock do DOM do Illustrator) antes da entrega, e documenta claramente que o arquivo editável vem da execução do script, não da abertura direta do SVG/PDF.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/4hm4di/ahkab-illustrator-text/tree/HEAD/ahkab-illustrator-text

Installed? Explore more Imagem skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Ahkab Illustrator Text — texto nativo do Illustrator (um parágrafo = uma caixa de texto)

Por Ahmad Abdullahi Kabeer · Graphcent

Por que

O Illustrator sempre divide o texto importado de SVG e PDF em um objeto de texto por linha, agrupados. Nenhuma marcação SVG impede isso. Portanto: mantenha as páginas SVG para a arte e reconstrua TODO o texto nativamente dentro do Illustrator com um script de construção ExtendScript (.jsx) que o usuário executa uma vez (File > Scripts > Other Script...). O script salva um .ai genuíno.

Fluxo de trabalho

  1. Gravar texto ao gerar páginas. Onde quer que o gerador de página emita <text>, armazene também um registro por página: {id, kind: 'area'|'point', layer: '05', x, y (primeira linha de base, SVG y para baixo, pt), w (largura da caixa), lead, n (contagem de linhas quebradas), size, anchor: 'start'|'middle'|'end', ls (espaçamento entre letras pt), runs: [[texto, peso, '#RRGGBB'], …], name}
    • Parágrafos quebrados (e títulos com várias linhas) -> area. Rótulos de linha única, números, cartolas -> point.
    • Use exatamente os mesmos x/y/largura/entrelinhamento usados para desenhar o SVG, para que as posições correspondam ao PDF.
    • Dê nomes significativos (ex.: Nome — slot 01, Biografia — slot 04).
  2. Requisitos da página SVG: arquivos pages/NN_Name.svg (NN = número da página); grupos de alto nível id="L01_Backgrounds", L02_… etc. com data-name = nome de camada legível. Mantenha o texto também nos SVGs (fallback + renderização PDF).
  3. Gerar o .jsx incorporando os registros como um literal de objeto JS (json.dumps(..., ensure_ascii=True) — ExtendScript é ES3, não precisa de JSON.parse).
  4. Teste em seco o .jsx gerado no Node contra um mock do DOM do Illustrator (app, documents.add, open, textFrames.areaText/pointText, pathItems.rectangle, artboards, textFonts.getByName, enums). Verifique: contagens de quadros de área/ponto, cada quadro tem conteúdo + fonte + nome, coordenadas do retângulo são números, artboards = páginas.
  5. Documente no README que o .ai editável vem da execução do script; abrir SVG/PDF diretamente ainda dá linhas divididas. Diga claramente que o script foi testado em um mock, não no próprio Illustrator.

Lógica central do script (ExtendScript)

#target illustrator
var PAGE_TEXT = /* {"1":[registros…], "2":[…] } */;
app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;
app.coordinateSystem = CoordinateSystem.DOCUMENTCOORDINATESYSTEM;   // y aumenta para cima
// 1) doc = app.documents.add(DocumentColorSpace.RGB, W, H); criar camadas nomeadas (doc.layers.add() adiciona no topo).
// 2) por página p: retângulo do artboard [left, top, left+W, top-H] com left = col*(W+GAP), top = -row*(H+GAP).
//    src = app.open(svgFile); deletar texto importado:
//      for (var i = src.textFrames.length-1; i >= 0; i--) src.textFrames[i].remove();
//    encontrar grupos/sub-camadas cujo nome corresponda a /^L?(0[1-9])/ e duplicá-los na camada correspondente:
//      item.duplicate(targetLayer, ElementPlacement.PLACEATBEGINNING).translate(left - sr[0], top - sr[1]);
//    (iterar container.pageItems, filtrar typename === 'GroupItem'; não comparar objetos DOM com ===)
//    src.close(SaveOptions.DONOTSAVECHANGES); depois buildText(PAGE_TEXT[pageNo], left, top).
var FONT_CANDIDATES = { 300:["Kreadon-L","Outfit-Light"], 400:["Kreadon-R","Kreadon-M","Outfit-Regular"],
  500:["Kreadon-M","Outfit-Medium"], 600:["Kreadon-Demi","Kreadon-D","Outfit-SemiBold"],
  700:["Kreadon-B","Outfit-Bold"], 800:["Kreadon-EB","Outfit-ExtraBold"] };  // colocar os nomes PostScript do projeto primeiro
function fontFor(w){ var n=FONT_CANDIDATES[w]||FONT_CANDIDATES[400]; for(var i=0;i<n.length;i++){ try{ return app.textFonts.getByName(n[i]); }catch(e){} } return null; }
function rgb(h){ var c=new RGBColor(); h=h.replace('#',''); c.red=parseInt(h.substr(0,2),16); c.green=parseInt(h.substr(2,2),16); c.blue=parseInt(h.substr(4,2),16); return c; }
function styleRange(r,w,size,col,lsPt,lead){ var a=r.characterAttributes, f=fontFor(w); if(f) a.textFont=f; a.size=size;
  if(lead){ a.autoLeading=false; a.leading=lead; } a.fillColor=rgb(col); a.tracking=Math.round((lsPt||0)/size*1000); }
function buildText(recs, left, top){
  for (var i=0;i<recs.length;i++){ var t=recs[i], layer=layerByKey[t.layer]||layerByKey['06'], s='', tf;
    for (var r=0;r<t.runs.length;r++) s+=t.runs[r][0];
    if (t.kind==='area'){ var lead=t.lead||t.size*1.4;
      var rect=layer.pathItems.rectangle(top-(t.y-lead), left+t.x-1, t.w+2, (t.n+1)*lead);  // folga de +1 linha para trocas de fonte
      tf=layer.textFrames.areaText(rect); tf.contents=s; styleRange(tf.textRange,t.runs[0][1],t.size,t.runs[0][2],t.ls,lead);
      try{ tf.firstBaseline=FirstBaselineType.LEADING; }catch(e){ tf.translate(0,-(lead-t.size*0.95)); }
    } else { tf=layer.textFrames.pointText([left+t.x, top-t.y]); tf.contents=s; styleRange(tf.textRange,t.runs[0][1],t.size,t.runs[0][2],t.ls,null); }
    tf.textRange.paragraphAttributes.justification = t.anchor==='middle'?Justification.CENTER:(t.anchor==='end'?Justification.RIGHT:Justification.LEFT);
    // spans ricos: para execuções após a primeira cujo peso/cor diferem, estilizar caracteres [pos, pos+len):
    //   try { var rr=tf.textRange.characters[pos]; rr.length=len; styleRange(rr,…); } catch(e){ loop por caractere }
    tf.name = t.name || t.id; } }
// 3) IllustratorSaveOptions {pdfCompatible:true, compressed:true, embedLinkedFiles:true}; doc.saveAs(File(root+'/<Nome>.ai'));
//    Mostrar um alerta final com quaisquer notas (ex.: fontes faltantes). Envolver tudo em try/catch e restaurar userInteractionLevel.

Verificações antes de entregar

  • O teste em seco do mock passa; a contagem de parágrafos corresponde ao número de blocos de texto quebrados no gerador.
  • As caixas de área usam a largura original (+2pt) para que as quebras de linha correspondam ao PDF; com uma fonte de substituição mais larga, elas refluem dentro da caixa.
  • O README explica: execute o script para obter o .ai editável; Find/Replace Font se trocar as fontes; o script não foi executado dentro do Illustrator pelo Claude.

Habilidades Relacionadas