1

I have already written a code to extract content from a csv file and add each chunk of text on each layer of illustrator. At the moment I could add content for fixed artboard. But I need to add separate art board while keeping the width 23mm. So, artboard height similar to content height of the particular layer. I'm getting an error "TypeError: text.parentArtboard is undefined" Kindly help me to resolve the above issue.

var csv = '''clipName,Trans,fname
A1 ,test Text1,A1_ENG
A2 ,Pigment dyed fabric w.,A2_ENG
A3 ,UPF 50+ fabric. Only covered areas are protected. To maintain this level of protection the garment must be rinsed in fresh water after each use.,A3_ENG
A4 ,Light colours may become transparent when wet,A4_ENG'''



/* var csv_file = File.openDialog();
csv_file.open("r")
var csv = csv_file.read();
csv_file.close() */
var lines = csv.split("\n");


// MAIN -------------------------------------------------------------

// make character styles
var FONT1 = make_style("font1", "ArialMT", 5.5);

// process lines
for (var i=1; i<lines.length; i++) {
    var data = get_data_from(lines[i]);
    make_layer(data.name)
    var text = make_text(data.contents);
    
    apply_styles(text);
    
    put_in_center(text);
     // Create a new artboard
    //artboard()
}

// END

// functions --------------------------------------------------------

function make_style(style_name, font_name, size) {
      // try to add a new style
    try { var style = app.activeDocument.characterStyles.add(style_name) } 
    // or pick a style with the same name if it exists already
    catch(e) { var style = app.activeDocument.characterStyles.getByName(style_name) }
    
    //var style = app.activeDocument.characterStyles.add(style_name);
    style.characterAttributes.size = size;
    style.characterAttributes.textFont = textFonts.getByName(font_name);
    return style;
}

function addArtboard(text, artboardWidth ) {
    // Get all layers in the text
    var layers = text.layers;
    
    // Set the artboard width to 23
    var artboard = text.parentArtboard;
    artboard.width = artboardWidth;
    
    // Declare variable to keep track of content height
    var contentHeight = 0;
    
    // Loop through all layers
    for (var i = 0; i < layers.length; i++) {
        var layer = layers[i];
        
        // Position the layer
        layer.x = 0;
        layer.y = contentHeight;
        
        // Update content height
        contentHeight += layer.height;
    }
    
    // Set the artboard height based on the content height
    artboard.height = contentHeight;
}

function get_data_from(line) {
    
    var arr = line.split(",");
    var fname = arr[2]
    var VL1   = arr[0];
    var VL2   = arr[1];
    //alert("Your message here...", fname);
    //return {"name":Fname, "contents":[VL1, VL2, VL3, VL4,VL5, VL6, VL7, VL8, VL9]};
    return {"name":fname, "contents":[VL2]};
}
//string.includes(substring)
function rearrangeText(text, artboardWidth) {
  // Split the text into words
  var words = text.split(" ");
  var rearrangedText = "";
  var currentLine = "";
  
  // Loop through each word
  for (var i = 0; i < words.length; i++) {
    var word = words[i];
    // If the current line + the next word would exceed the artboard width
    if (currentLine.length + word.length > artboardWidth) {
      // Add the current line to the rearranged text
      rearrangedText += currentLine + "\n";
      // Reset the current line
      currentLine = "";
    }
    // Add the word to the current line
    currentLine += word + " ";
  }
  // Add the last line to the rearranged text
  rearrangedText += currentLine;
  return rearrangedText;
}

    
function make_layer(layer_name) {
    var new_layer = app.activeDocument.layers.add();
    new_layer.name = layer_name;
}

function make_text(array) {
    var text = app.activeDocument.textFrames.add();
        text.contents = rearrangeText(array.join("\n"),23);
         addArtboard(text.contents,23) //Try to add new artboar based on the text content size
    return text;
}
function artboard() {
    var layers = app.activeDocument.layers;
    var textLayers = [];
    for (var i = 0; i < layers.length; i++) {
        if (layers[i].kind == LayerKind.TEXT) {
            textLayers.push(layers[i]);
        }
    }
    var width = 23; // width in mm
    for (var i = 0; i < textLayers.length; i++) {
        var textLayer = textLayers[i];
        var textBounds = textLayer.bounds;
        var artboardWidth = width * app.activeDocument.rulerUnits;
        var artboardHeight = textBounds[3] - textBounds[1];
        var artboard = app.activeDocument.artboards.add(textBounds);
        artboard.width = artboardWidth;
        artboard.height = artboardHeight;
        textLayer.move(artboard);
    }
}


function to_center(artboard, item){
    var artboard_x = artboard.artboardRect[0] + artboard.artboardRect[2];
    var artboard_y = artboard.artboardRect[1] + artboard.artboardRect[3];
    var x = (artboard_x - item.width)/2;
    var y = (artboard_y + item.height)/2;
    item.position = [x, y];
}

function apply_styles(text) {
    // not the best piece of code, I'm sure it can be done better
    text.textRange.paragraphAttributes.justification = Justification.CENTER;
    FONT1.applyTo(text.textRange);

 
}

function put_in_center(obj) {
    var rect = app.activeDocument.artboards[0].artboardRect;
    var page_w = rect[2] - rect[0];
    var page_h = rect[1] - rect[3];
    var shift_x = page_w/2 - obj.width/2;
    var shift_y = -page_h/2 + obj.height/2;
    obj.position = [rect[0] + shift_x, rect[1] + shift_y];
}

When I remove the function addArtboard(text, artboardWidth ) all content will be nicely rearrange based on the width of the artboard. But, artboard size doesn't change due to the code error. Experts kindly help me with this. Image of the content arrangement on illustrator layers are mention below

enter image description here

hiran
  • 69
  • 5
  • The credits is here: https://stackoverflow.com/a/67323870/14265469 Probably the problem is unresolvable since there is no accepted answer after twenty months. – Yuri Khristich Jan 29 '23 at 13:50
  • Hi Yuri, Recently I got proficient to accept the answer. I have accepted the answer now and sorry for the inconvenience . This is bit different as the each layer has different text height. Each layer has to be saved in different artboard based on the text height. But artboard width has to be 25mm for all and text content must be distributed within 23mm and align both vertically and horizontally. . – hiran Jan 29 '23 at 14:05
  • Yuri Khristich Are you still active on discord ? – hiran Jan 29 '23 at 14:23
  • I have the Discord but right now I'm not ready to spent much time on it. Could you please post a screenshot what the desired output should look like? As far as I understand the task can be boiled down to the function that puts the created texts from these separate layers to separate artboards. And each artboard should have the height depending on the height of its text. Is it correct? – Yuri Khristich Jan 29 '23 at 19:57

1 Answers1

1

If understand the task correctly you can add this function into the code:

function distribute_texts_and_create_artboards() {

    const mm    = 2.83465;   // mm/pt
    var shift_x = 30 * mm;   // horizontal shifts for texts
    var width   = 25 * mm;   // artboard width

    var doc = app.activeDocument;
    var frames = doc.textFrames;

    // move all the frames and create an artboard for the every text frame

    for (var i=0; i<frames.length; i++) {

        var frame = frames[i];
        frame.translate(shift_x * i, 0); // move the frame horizontally

        var frame_x1 = frame.geometricBounds[0];
        var frame_y1 = frame.geometricBounds[1];
        var frame_x2 = frame.geometricBounds[2];
        var frame_y2 = frame.geometricBounds[3];

        var artboard_x = frame_x1 - (width - frame.width)/2;
        var artboard_y = frame_y1;
        var artboard_w = frame_x2 + (width - frame.width)/2;
        var artboard_h = frame.height;

        var artboard_rect = [artboard_x, artboard_y, artboard_w, -artboard_h];
        var new_ab = doc.artboards.add(artboard_rect); // create a new artboard
        new_ab.name = frame.parent.name; // artboard name = layer name

        // frame.position = [frame.position[0], frame.position[1]/2]; // to centred the frame vertically
    }

    doc.artboards[0].remove(); // delete the old first artboard
}

and call it at the end of the 'MAIN' section:

...
}

distribute_texts_and_create_artboards();

// END

...

and comment out the line:

// put_in_center(text);

It should give you the layout as follows:

enter image description here

Probably it makes sense to tweak the vertical position of the frames of the artboards a bit. But since you haven't provided an example of desired result I left it as is, for now. Let me know if you need it or uncomment the line:

frame.position = [frame.position[0], frame.position[1]/2];

And you don't need the functions addArtboard() and artboard() anymore in the code.


Update

Here is the function that saves all the text frames from layers as separate PDFs into the same folder as current AI file. File names are the same as layer names.

function save_layers_as_pdfs() {
    var doc = app.activeDocument;

    // pdf options
    var options = new PDFSaveOptions();
    options.compatibility = PDFCompatibility.ACROBAT5;
    options.generateThumbnails = true;
    options.preserveEditability = false;
    options.preset = "[Smallest File Size]";

    var mm = 2.83465;     // mm/pt
    var width = 25 * mm;  // artboard width

    var layers = doc.layers;
    for (var i=0; i<layers.length; i++) {
        var layer = layers[i];
        layer.hasSelectedArtwork = true;
        if (app.selection.length == 0) continue;
        app.copy();
        app.selection = null;

        // create a new file
        var file = File(doc.path + '/' + layer.name + '.pdf');
        var new_doc = app.documents.add();
        app.paste();

        // change the artboard size
        var frame = new_doc.textFrames[0];

        var frame_x1 = frame.geometricBounds[0];
        var frame_y1 = frame.geometricBounds[1];
        var frame_x2 = frame.geometricBounds[2];
        var frame_y2 = frame.geometricBounds[3];

        var artboard_x = frame_x1 - (width - frame.width)/2;
        var artboard_y = frame_y1;
        var artboard_w = frame_x1 + frame.width + (width - frame.width)/2;
        var artboard_h = frame_y1 - frame.height;

        doc.artboards[0].artboardRect = [artboard_x, artboard_y, artboard_w, artboard_h];

        // save a pdf and close document
        new_doc.saveAs(file, options);
        new_doc.close(SaveOptions.DONOTSAVECHANGES);
    }
}

You can call this function at the end of the MAIN section.

The result:

enter image description here

And make sure your csv data is correct: the separators, quote marks, tabulations, etc.

Yuri Khristich
  • 13,448
  • 2
  • 8
  • 23
  • Thank you very much for your collaboration and the dedication. Once you generated above clip it has to be horizontally and vertically centered. Finally each clip has to be saved as _pdf_ with the **Layer name**. I tried to upload images but thee is no proficient to upload it – hiran Jan 30 '23 at 04:46
  • I have tested above code . But it gave me error 1200 at line #82 (' doc.artboards.add(artboard_rect); // create a new artboard'). Once I give OK for the dialogue box All artboards were vanished. none of layer got save during the running process. Kindly help me on this . – hiran Jan 30 '23 at 05:38
  • Sorry, I can't reproduce the error1200 in on my side. Try to download and run my code: https://drive.google.com/file/d/1OTL3GTi7QVFCocHLwHGzqOtrxKm8OmeN/view?usp=share_link As for 'to save clips as pdfs' it can be done with no sweat. I did already something like this recently. Here: https://stackoverflow.com/a/75229454/14265469 (the function `save_sublayers()`) you can try to tweak it on your own, and I'm ready to help you. – Yuri Khristich Jan 30 '23 at 08:40
  • And you can export all artboards as separate PDF's with the free script `MultiExporter.jsx`, see here: https://graphicdesign.stackexchange.com/a/90828/163111 – Yuri Khristich Jan 30 '23 at 09:05
  • Yuri It's work for the given embedded csv details. But when I give real csv it give error . Is there any way I can send you the csv file ? can you share the mail if you don't mind. – hiran Jan 30 '23 at 09:38
  • You can download the csv file from below link. [link] (https://docs.google.com/spreadsheets/d/1dBngYVacD57BIo7tiiERHhVUd9DgBuYfAD-2HdUtiMw/edit?usp=sharing) – hiran Jan 30 '23 at 10:09
  • Looks like your data is broken. There is no 'fname' value on some lines (column 3). As soon as I remove the broken lines the script works more or less fine. And your CSV by the link has the non standard separator `|` so, if it's true for your CSV files, you have to correct one line in the function `get_data_from(line)` this way: `var arr = line.split('|');` – Yuri Khristich Jan 30 '23 at 11:47
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/251491/discussion-between-hiran-and-yuri-khristich). – hiran Jan 30 '23 at 12:17
  • Correct csv as follows [link](https://docs.google.com/spreadsheets/d/1Mai-PNK6qu3prLsc9x7a51LctLK8jL4hKlMBRNqfKdA/edit?usp=sharing) current output [link](https://drive.google.com/file/d/1krtjJ39p2ycifuc69G-WLyqeMNZG7qtJ/view?usp=share_link) – hiran Jan 30 '23 at 14:43
  • There are too many artboards. They don't fit into the pasteboard. I see two possible solutions: 1. Make several rows of artboards, say 50 artboards in the row. 2. If all you need is the separate PDFs, you don't even need to make the temporary AI file with so many artboards inside. The script could a layer into a new file, change the artboard size of the new file, save the new file as a PDF, and close it. And repeat this for the rest layers. What do you think? – Yuri Khristich Jan 30 '23 at 15:15
  • I think 2nd option would be fine. I have planed to go with separate artboards because sizes of clips are different one to another. If we can "layer into a new file, change the artboard size of the new file, save the new file as a PDF, and close it" would be ideal. How to proceed with it where to make the change ? – hiran Jan 30 '23 at 15:53
  • And as it turned out Illustrator has the limitation: a file can't have more than 100 artboards. – Yuri Khristich Jan 30 '23 at 16:15
  • So what would be best way of doing this .Kindly help me with that. – hiran Jan 30 '23 at 16:25
  • I just added the function to save all the layers of current AI file as separate PDFs with individual artboard sizes. Check it out – Yuri Khristich Jan 30 '23 at 16:47
  • Is this function added after the distribute_texts_and_create_artboards() – hiran Jan 30 '23 at 17:18
  • No, instead of the `distribute_texts_and_create_artboards()`. Basically you take your old script, remove from there all manipulations with artboards, you don't need them anymore, and add this new function. – Yuri Khristich Jan 30 '23 at 17:22
  • Actually it's as if there are two separate scripts. First one (your old script) makes the AI file from CSV data. Second one (my new function) makes from the layers of current AI file separate PDF files. – Yuri Khristich Jan 30 '23 at 17:31
  • My friend, If you don't mind can you share completed file via the google drive. Unfortunately my pdf's are not being generated. sorry for the inconvenience. according to the code all pdf's are being saved in the current folder where the main script is located. – hiran Jan 30 '23 at 17:46
  • 1
    No problem, here you go: https://drive.google.com/file/d/13w5zCi1o1ilQr1sL4sPW6E-67y2mucrC/view?usp=share_link I put into the one zip file all my files: JSX, AI, CSV and generated PDF files. The PDF files are being saved into the same folder where AI file is located. – Yuri Khristich Jan 30 '23 at 18:19
  • 1
    Your are a genius Yuri !!!. You are one of few person who help others during hardship without any return. Wishing you all the best my friend. – hiran Jan 31 '23 at 08:32
  • This is an additional thing. Is there possibility of generating such clips using python framework without using Adobe AI software ? Can we import any library related to Adobe AI and do such thing using python. – hiran Feb 01 '23 at 05:02
  • 1
    If you mean to take a CSV and make PDFs — I believe it can be done with Python. The problem, as far as I can tell, can be to change styes of the texts (bold, italics, size, etc). I think it's more promising to try HTML > PDF conversion with Javascript (Node.js) https://www.npmjs.com/package/html-to-pdf-js A script makes HTMLs from CSV and then convert the HTMLs into PDFs. – Yuri Khristich Feb 01 '23 at 11:00
  • 1
    Though Python has similar tools either https://stackoverflow.com/questions/23359083/how-to-convert-webpage-into-pdf-by-using-python So, the main trick is to create a proper HTML from the CSV. – Yuri Khristich Feb 01 '23 at 11:09
  • I want to add new font which is not in the Windows default font list. I have to change the font to Chinese with following code var FONT2 = make_style("font2", "MHeiGB18030", 5.5); and change FONT2.applyTo(text.textRange); But I'm getting error (No such element) at the line style.characterAttributes.textFont = textFonts.getByName(font_name); In addition I need to change the vertical line spacing to 0.5 font mention below link https://drive.google.com/file/d/1g7MBcRnrnqSA6rP8KX1Oj4koRsPpPc-Z/view?usp=sharing – hiran Feb 15 '23 at 12:59
  • Are you available – hiran Feb 16 '23 at 06:19
  • Font name should be `"MHeiGB18030Medium"` (so called postscript font name). You can set the vertical line spacing in your style if you add this line `style.characterAttributes.leading = size * 0.5;` in the function `make_style()`. – Yuri Khristich Feb 16 '23 at 07:01
  • I have used "MHeiGB18030Medium", but got the same error. Did you try it with same font ?. – hiran Feb 16 '23 at 07:59
  • Yes, I've tried `var FONT1 = make_style("font2", "MHeiGB18030Medium", 5.5)` (after installing the font in my system) and it changes the font just fine. But I have some troubles with slicing the the lines of your CSV, perhaps due another symbols for commas and spaces in Chinese I suspect. But I had no problem with the font name and the leading (the distance between lines). – Yuri Khristich Feb 16 '23 at 19:54
  • And I came across another glitch (perhaps it has something to do with Chinese): you have to apply the style twice with 'true' option: `FONT1.applyTo(text.textRange, true); FONT1.applyTo(text.textRange, true);`. – Yuri Khristich Feb 16 '23 at 20:29
  • You can download my JSX file and results here: https://drive.google.com/file/d/1eFGut5_Rgza5-VOoiPlYvjiGFKRL5T_a/view?usp=share_link as you can see, there are problems with slicing the lines. I don't know why it works this way. But otherwise it works as expected. – Yuri Khristich Feb 16 '23 at 20:36
  • And as it turned out, to make a custom leading you have to turn off the 'autoleading' in the style: `style.characterAttributes.autoLeading = false;` go figure. ) – Yuri Khristich Feb 16 '23 at 20:52
  • Hope you can see the Chinese letters are being moving out of the art board which has to be 25mm. this happens only for Chinese language. Can you fix this if you donj't mind . – hiran Feb 20 '23 at 06:48
  • I don't know Chinese (Mandarinian?). Probably I can figure out how to break the lines but I have no faintest idea if it will be grammatically correct. Can I insert the breaks between any hieroglyph? Actually the algorithm to break the lines looks rather weird. For this case it would be better to use rectangle text frames and put the texts inside the frames. What do you think? – Yuri Khristich Feb 20 '23 at 09:26
  • It would be good idea. Size of the Rectangle would be 25- (2) => 23mm size . Shall we give a try despite of the grammar of mandarin? Size should not exceed 23 mm – hiran Feb 20 '23 at 13:44
  • By the what is the best place that I should make the text createoutline() – hiran Feb 20 '23 at 14:05
  • It makes sense to outlined the text right before you save a document as PDF. You can do it with the line `text.createOutline();`. (Sometimes by some mystical reasons it's need to use this command twice to get all the text outlined). As for rectangle text frames, I'll try it soon. And you can try it by yourself. You can take a part of the solution here: https://stackoverflow.com/a/75228383/14265469 – Yuri Khristich Feb 20 '23 at 20:44
  • Here https://drive.google.com/file/d/1XhyvsKnYGN-6cmsi4bPV_RMhlSxS9gzd/view?usp=share_link is the variant of the script that creates text boxes. – Yuri Khristich Feb 21 '23 at 20:14
  • Many thanks for your effort. It works fine for the data in the text file it self. But, when I use csv file upload command . I'm getting error. file in the below link. kindly have a look. https://drive.google.com/file/d/13KWwUNDIDP2gA9pO-ozL8xdu0TBvG5VP/view?usp=share_link – hiran Feb 24 '23 at 05:03
  • When I tried with other languages it popup error at line 165 (Required value is missing) new_layer.name = layer_name – hiran Feb 24 '23 at 06:20
  • Will you able to check this. – hiran Feb 25 '23 at 02:58
  • done. sorry for the inconvenience. – hiran Feb 25 '23 at 09:56
  • The JSX file (from your link) works fine with the Chinese CSV file (from you link). It makes 105 PDF files in the output folder. The only correction in the script is to comment out the line #46 `distribute_texts_and_create_artboards();` since your data has more than 100 lines and Ilustrator can't create more than 100 artboards. But you said you have the error with English CSV data, do you? I see no English CSV file by your link. – Yuri Khristich Feb 25 '23 at 11:19
  • I added the line `csv_file.encoding = 'utf8';` just in case. And I found one little error in the code (sometimes one last hieroglyph disappears). Corrected script and output you can download here: https://drive.google.com/file/d/1a75HmVTxImBt7KmjME1OuQGMbo64ZuQD/view?usp=share_link – Yuri Khristich Feb 25 '23 at 11:33