├── LICENSE ├── README.md ├── batch_idml_editor.py └── example ├── business_cards_template.idml └── my_data_file.csv /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Involution Studios 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Batch-IDML-Generator 2 | ==================== 3 | Sometimes you want to incorporate data into your designs. This is a simple tool that lets you batch generate IDML files using data from a CSV file based on a template. 4 | 5 | Example Usage 6 | ============= 7 | In the `example` folder, you will find a sample IDML file for a business card. In that file, we added `###first###`, `###last###`, `###title###`, and `###email###` as fields which should be driven by data. 8 | 9 | The `example/my_data_file.csv` file contains the names, titles, and e-mail addresses of the people we want to generate business cards for. 10 | 11 | In `batch_idml_editor.py`, we have changed the following variables to be paths to our files 12 | ```python 13 | #Path and Filename of IDML file (template), including ".idml". 14 | #change this 15 | idml = 'example/business_cards_template.idml' 16 | 17 | #Path and Filename of the CSV file (data), including ".csv" 18 | #change this 19 | csv_file = 'example/my_data_file.csv' 20 | ``` 21 | 22 | We have also created a mapping of the template fields in our IDML file (`###first###`, `###last###`, etc) to the fields in our CSV file 23 | 24 | ```python 25 | #The search strings we are using with the key from the CSV file that we want to replace it with. 26 | fields_to_replace = [{'search':'###first###', 'replace_key':'First Name'}, 27 | {'search':'###last###', 'replace_key':'Last Name'}, 28 | {'search':'###title###', 'replace_key':'Title'}, 29 | {'search':'###email###', 'replace_key':'E-mail'}] 30 | ``` 31 | 32 | Finally, we made a template string which is the filename for each of the exported IDML files 33 | ```python 34 | output_filename_template = '###first###-###last###-card' 35 | ``` 36 | 37 | To run this tool, open a terminal window in this directory and type: 38 | ``` 39 | python3 batch_idml_editor.py 40 | ``` 41 | 42 | The script should output: 43 | ``` 44 | Saving to output1397664993/... 45 | Ben-Salinas-card 46 | Eric-Benoit-card 47 | ``` 48 | 49 | Each time you run the script, a new output directory will be created. Inside that directory, you will find IDML files that should data representing each row of the CSV file. 50 | 51 | After you have your directory of IDML files, you can use a script like the one at http://www.kahrel.plus.com/indesign/batch_convert.html to do a batch convert to PDF or other format. 52 | 53 | Why Not InDesign DataMerge? 54 | =========================== 55 | The DataMerge feature in Adobe InDesign and Illustrator is very powerful and allows you to do basically the same thing(and more!) within the app itself. We have found that this is a great option, but has some limits on the number of rows it can process and doesn't play well with multi-page documents. It also doesn't let you create meaningful filenames for the resulting files. In particular, we built this to be able to export several hundred PDFs with custom filenames based on the content of the data. 56 | 57 | How it Works 58 | ============ 59 | When you unzip an IDML file, there are several folders that contain XML files. One of those folders, `Stories`, has all of your content. For each row of your CSV, this script unzips the IDML file which serves as the template, performs a find and replace an all of the files in the `Stories` folder, and then zips the new sets of files as an IDML file. It doesn't interact with InDesign in any way, just with the IDML files themselves. 60 | 61 | Dependencies 62 | ============ 63 | * python3 64 | * InDesign 65 | 66 | Note that this has only been tested on Mac OSX. There will likely need to be a few small changes to make it work on Windows. 67 | 68 | Contributing 69 | ============ 70 | To make changes or improvements, fork this repo. Pull requests accepted. Feel free to create issue tickets to ask questions about how it works or suggest improvements. 71 | 72 | Ways to Improve: 73 | * Use the IDML SDK that Adobe provides to validate the IDML file (right now no validation takes place) 74 | * Add more robust templating options (such as Handlebars or Mustache support) 75 | * Separate the run-time data from the portion of the script that doesn't need changing 76 | * Add more error handling. 77 | -------------------------------------------------------------------------------- /batch_idml_editor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import csv 3 | import glob 4 | import os 5 | import shutil 6 | import time 7 | import codecs 8 | 9 | #Path and Filename of IDML file (template), including ".idml". 10 | #change this 11 | idml = 'example/business_cards_template.idml' 12 | 13 | #Path and Filename of the CSV file (data), including ".csv" 14 | #change this 15 | csv_file = 'example/my_data_file.csv' 16 | 17 | #Separator for fields in the CSV file (in case you want a 'tab', just change to '\t') 18 | #change this 19 | separator=',' 20 | 21 | #The search strings we are using with the key from the CSV file that we want to replace it with. 22 | fields_to_replace = [ 23 | {'search':'###first###', 'replace_key':'First Name'}, 24 | {'search':'###last###', 'replace_key':'Last Name'}, 25 | {'search':'###title###', 'replace_key':'Title'}, 26 | {'search':'###email###', 'replace_key':'E-mail'} 27 | ] 28 | 29 | output_filename_template = '###first###-###last###-card' 30 | 31 | #Unzip the IDML file and save it locally 32 | 33 | template = 'template/' 34 | shutil.unpack_archive(idml,template, 'zip') 35 | 36 | tld = 'output'+str(int(time.time()))+'/' 37 | 38 | print('Saving to '+tld+'...') 39 | 40 | #open the CSV file 41 | with codecs.open(csv_file, 'r', encoding='utf-8-sig') as csvfile: 42 | #construct our reader for the CSV file 43 | reader = csv.reader(csvfile, delimiter=separator) 44 | #read the header line 45 | header = next(reader) 46 | 47 | for row in reader: 48 | #For each row in our CSV 49 | 50 | #make our filename 51 | filename = output_filename_template 52 | for rep in fields_to_replace: 53 | try: 54 | filename = filename.replace(rep['search'], row[header.index(rep['replace_key'])]) 55 | except ValueError: 56 | print("\nPlease check that {} is encoded using 'utf-8' or try using 'utf-8-sig'".format(csv_file)) 57 | break 58 | 59 | #print it out to the console 60 | print(filename) 61 | 62 | #Make a copy of our template 63 | try: 64 | shutil.copytree(template,'tmp/') 65 | except FileExistsError: 66 | #'tmp/' may be leftover from a previous attempt, or your OS handles file deletion poorly 67 | if os.path.isdir('tmp')==True: 68 | dlt = input("\ntmp/ directory exists\nYou may need to re-run the program \nRemove tmp/(y/n)?").lower() 69 | if dlt != 'n': 70 | shutil.rmtree('tmp/') 71 | else: 72 | print("\nPlease manually remove 'tmp/' and try again") 73 | break 74 | 75 | stories = os.listdir(template+'Stories') 76 | #for each file that we want to modify 77 | for entry in stories: 78 | #open a new file that we will write to 79 | try: 80 | with codecs.open('tmp.tmp', "w", encoding='utf-8') as fout: 81 | #open the file that we want to read from 82 | with codecs.open(template+'Stories/'+entry, "r", encoding='utf-8') as fin: 83 | #on every line 84 | for line in fin: 85 | line2 = line 86 | #for every thing we want to search for 87 | 88 | for rep in fields_to_replace: 89 | #see if this line has anything to replace and replace it with data from our CSV file 90 | line2 = line2.replace(rep['search'], row[header.index(rep['replace_key'])]) 91 | #write this line 92 | fout.write(line2) 93 | except PermissionError: 94 | break 95 | 96 | 97 | shutil.move('tmp.tmp', 'tmp/'+'Stories/'+entry) 98 | 99 | 100 | #make a .zip of the folder 101 | shutil.make_archive(tld+filename, 'zip', 'tmp/') 102 | #change the name to .idml 103 | shutil.move(tld+filename+'.zip', tld+'/'+filename+'.idml') 104 | #delete the folder we were working in 105 | shutil.rmtree('tmp/') 106 | 107 | 108 | 109 | #delete our unzipped IDML directory 110 | shutil.rmtree(template) 111 | 112 | -------------------------------------------------------------------------------- /example/business_cards_template.idml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goinvo/BatchIDMLGenerator/bb0704ca6065f600420f312046920732599c929b/example/business_cards_template.idml -------------------------------------------------------------------------------- /example/my_data_file.csv: -------------------------------------------------------------------------------- 1 | First Name,Last Name,Title,E-mail 2 | Ben,Salinas,Engineer/Designer,ben@goinvo.com 3 | Eric,Benoit,Designer,eric@goinvo.com --------------------------------------------------------------------------------