Copy link to clipboard
Copied
Hi all,
Quite often I have to write scripts that use data originated from an Excel worksheet. I (and most scripters) believed that the easiest approach was to use a CSV-file exported from Excel. However, this takes an extra step for the user so I decided to write a function that opens an Excel book in background, reads the data from its first spreadsheet and returns array.
The function has three arguments:
var excelFilePath = "D:\\My Test Folder\\SampleBook.xlsx";


The function (current version):
Main();
function Main() {
// The platform-specific full path name for the xlsx-file -- fsName
// If you pass it as a string, make sure to double the backslashes in the path like in the line below
var excelFilePath = "D:\\My Test Folder\\SampleBook.xlsx";
// [Optional] the character to use for splitting the columns in the spreadsheed: e.g. semicolon (;) or tab (\t)
// If it isn't set, semicolon will be used by default
var splitChar = ";";
// [Optional] the worksheet number: either string or number. If it isn't set, the first worksheet will be used by default
sheetNumber = "1";
var data = GetDataFromExcelPC(excelFilePath, splitChar, sheetNumber); // returns array
}
function GetDataFromExcelPC(excelFilePath, splitChar, sheetNumber) {
if (typeof splitChar === "undefined") var splitChar = ";";
if (typeof sheetNumber === "undefined") var sheetNumber = "1";
var vbs = 'Public s, excelFilePath\r';
vbs += 'Function ReadFromExcel()\r';
vbs += 'Set objExcel = CreateObject("Excel.Application")\r';
vbs += 'Set objBook = objExcel.Workbooks.Open("' + excelFilePath + '")\r';
vbs += 'Set objSheet = objExcel.ActiveWorkbook.WorkSheets(' + sheetNumber + ')\r';
vbs += 'objExcel.Visible = False\r';
vbs += 'matrix = objSheet.UsedRange\r';
vbs += 'maxDim0 = UBound(matrix, 1)\r';
vbs += 'maxDim1 = UBound(matrix, 2)\r';
vbs += 'For i = 1 To maxDim0\r';
vbs += 'For j = 1 To maxDim1\r';
vbs += 'If j = maxDim1 Then\r';
vbs += 's = s & matrix(i, j)\r';
vbs += 'Else\r';
vbs += 's = s & matrix(i, j) & "' + splitChar + '"\r';
vbs += 'End If\r';
vbs += 'Next\r';
vbs += 's = s & vbCr\r';
vbs += 'Next\r';
vbs += 'objBook.close\r';
vbs += 'Set objBook = Nothing\r';
vbs += 'Set objExcel = Nothing\r';
vbs += 'End Function\r';
vbs += 'Function SetArgValue()\r';
vbs += 'Set objInDesign = CreateObject("InDesign.Application")\r';
vbs += 'objInDesign.ScriptArgs.SetValue "excelData", s\r';
vbs += 'End Function\r';
vbs += 'ReadFromExcel()\r';
vbs += 'SetArgValue()\r';
app.doScript(vbs, ScriptLanguage.VISUAL_BASIC, undefined, UndoModes.FAST_ENTIRE_SCRIPT);
var str = app.scriptArgs.getValue("excelData");
app.scriptArgs.clear();
var tempArrLine, line,
data = [],
tempArrData = str.split("\r");
for (var i = 0; i < tempArrData.length; i++) {
line = tempArrData;
if (line == "") continue;
tempArrLine = line.split(splitChar);
data.push(tempArrLine);
}
return data;
}
I wrote and tested it in InDesign CC 2017 and Excel 2016 (Version 16) on Windows 10. I wonder if/how it works in other versions of Excel. Also, I'd like to get some feedback: bug reports, suggestions and new ideas.
Here's the permanent link on my site.
— Kas
Copy link to clipboard
Copied
One last thing: on a Mac, how do I have to express the file name? I tried many combinations last night, none worked. However, getDataFromExcelMac worked on the open excel doc.
I tried to follow this indication
// If you pass it as a string, make sure to double the backslashes in the path like in the line below
var excelFilePath = "Macintosh HD:Users:me:Desktop:SampleBook.xlsx";
I do not find an example of doubled backslashes below. Again, thanks for your patience.
Paul
Copy link to clipboard
Copied
And, last of all:
I bet I'll have to modify this in NormalizeData:
| var excelFilePath = excelFile.fsName.replace(/\\/g, "\\\\"); |
because I'm on a Mac. Sorry for the lameness.
Copy link to clipboard
Copied
Ok, I "figured it out".
var result= GetDataFromExcelMac("myFolder:Desktop: SampleBook.xlsx", "|", ";", 2);
but then I tried table.contents=result
and got "expected array of strings... but received... followed by a list of the contents of the table in excel set in parentheses and separated by commas.
Getting closer, at least.
Copy link to clipboard
Copied
Dear Kasyan,
Everything works perfectly, and has been for a while, but I noticed that I am losing text formatting when I get data from Excel. I have tried adjusting the settings in clipboard handling and I am still losing the bold and italics. Is there a setting that I can adjust or a modification I can make to the script to preserve formatting? Sorry to reappear. I had hoped to leave you in peace. Thanks for all the help yet again.
Paul
Copy link to clipboard
Copied
I have been trying to come up with a solution. Do you think finding all italicized and bold text in excel and tagging it so that I can find and replace with style in indesign is a good approach? It's all I could come up with.
Paul
Copy link to clipboard
Copied
Hi Paul,
Are you changing text using the contents property?
If so, try to use find-change Text/GREP instead.
— Kas
Copy link to clipboard
Copied
pjleveno wrote
I have been trying to come up with a solution. Do you think finding all italicized and bold text in excel and tagging it so that I can find and replace with style in indesign is a good approach? It's all I could come up with.
Paul
My function can return only plain text. For my scripting tasks I needed only contents. Anyway, arrays can't contain formatted text.
If you want to transfer formatting from Excel to InDesign, try another approach:
— Kas
Copy link to clipboard
Copied
Thanks, Kasyan.
Sadly, placing the spreadsheets with
var textbookSheet = app.excelImportPreferences.sheetIndex=2;
var range = app.excelImportPreferences.rangeName = "some range";
doc.textFrames.item("Source").place(File(myExcelFiles));
was the problem originally. When I specify the range on some sheets, Indesign crashes and if I don't specify the range, indd places an excessive number of empty rows and columns. It did this in the UI as well, so your script saved me a ton of trouble mainly because of how you get the sheet indices and the "used range of theSheet".
My only thought was somehow tagging the italicized or bold text in excel like <I>words</I> by find/replace, then grep find/change in indesign to style the text appropriately. However, I see that find/change in excel for Mac is not at all as simple as it is for me in Indesign.
So far I have found this:
if font style of font object of cell (theCol & theRow) contains "Italic" then
set name of font object of cell (theCol & theRow) to "Calibri"
If I could do something like that for characters and not cells in excel and instead of setting the font, I could modify the text of the found styled characters, that could be a way to continue to use getDataFromExcel.
Otherwise, I'll keep trying to figure out why rangeName crashes indesign. Some sheets just won't let me modify the rangeName, either in import Options in the UI or by way of javascript. I was so close! but italics and bold get me once again!
Paul
if font style of font
end if
Copy link to clipboard
Copied
Kasyan,
Possibly of interest, I just found this from November 19th of last year on the list of "Fixed Issues in InDesign":
I don't know what they mean by incorrect exactly, but I do know that I have been having problems precisely in this area.
Maybe I could get sheet names and active ranges via your script and place formatted tables using that information.
I currently get the count of the sheets this way:
as += 'set listSize to count of mySheets\r'
Paul
Copy link to clipboard
Copied
Kasyan,
I just discovered that used range is tricky. Indesign does find used range when placing excel docs, but used range can include many empty rows and columns because the cells may have been formatted in one way or another. So now I'm wondering how to find the true used range. I found this online.
Sub PickedActualUsedRange()
Range("A1").Resize(Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlValues).Row, _
Cells.Find(What:="*", SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, LookIn:=xlValues).Column).Select
End Sub
This finds the last cell that actually has content in it. If I could use this (as AppleScript) and feed the actual used range to indesign place rangeName, maybe I'd be set. Does this seem like a good direction to go?
Paul
Copy link to clipboard
Copied
My only thought was somehow tagging the italicized or bold text in excel like <I>words</I> by find/replace, then grep find/change in indesign to style the text appropriately. However, I see that find/change in excel for Mac is not at all as simple as it is for me in Indesign.
Warning! I know almost nothing about Excel. I studied how to script it , but only what I needed at that moment.
As far as I know, Excel has less find-replace options so, unlike in Indy, you can't find any text in italic and add tags before and after found text.
Possibly of interest, I just found this from November 19th of last year on the list of "Fixed Issues in InDesign":
InDesign crashes on importing excel file via script when incorrect sheet name or range is set.
You can skip this step by placing it manually. Does it crash in another version?
Also, you can place the whole spreadsheet in a small text frame. As far as I remember, you can search in overset text without any issues. I don't think it would take much longer to process the whole spreadsheet.
I just discovered that used range is tricky. Indesign does find used range when placing excel docs, but used range can include many empty rows and columns because the cells may have been formatted in one way or another. So now I'm wondering how to find the true used range. I found this online.
Sub PickedActualUsedRange()
Range("A1").Resize(Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlValues).Row, _
Cells.Find(What:="*", SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, LookIn:=xlValues).Column).Select
End Sub
This finds the last cell that actually has content in it. If I could use this (as AppleScript) and feed the actual used range to indesign place rangeName, maybe I'd be set. Does this seem like a good direction to go?
I don't see how you can fetch formatted text from Excel to Indy otherwise than placing it.
What are you going to do with the range selected in Excel: copy and paste it in Indy?
I have no idea whether it's good direction to go or not: you have to experiment with this. It may take days, weeks, months, or even till the end of the universe until you achieve the desired result.
— Kas
Copy link to clipboard
Copied
I'm thinking end of universe as the likely endpoint. I have gotten useful ranges now. My hope is to take the real used ranges from excel and use them as the range names in the placement in indesign. I'm almost there, I believe. I have a wacky AppleScript that gets the real ranges, if you're interested in seeing it, though I'm not usually very happy to publish poorly written scripts.
Once I line this up properly, I hope to place the formatted tables, as you suggested. If only excel find/replace resembled indesign find and replace, then I could use your script after setting tags around styled text in excel. Not to be.
Paul
Copy link to clipboard
Copied
Kasyan,
I finished it before the end of the universe. I now get the real used range, feed that into my place range name and then place just the content that I want in indesign. I then perform all the text normalizing and style prepping that I would normally. It seems this has solved the problem that started me down this road in the beginning: indesign place via script crashing upon selected range names.
If you're ever interested in any of this, please let me know. I owe you guys a lot for all the help.
Thanks again!
Paul
Copy link to clipboard
Copied
Glad to hear you finally sorted it out on your own!
Yes, I am interested to look into your solution, but can do this later: this week I am totally busy with work.
In my opinion, once you solved a problem, it's always a good idea to post the solution for the future generations.
— Kas
Copy link to clipboard
Copied
Kasyan,
Latest issue, which you've seen before, I think, because I saw you responded online. The question has to do with table formatting options. I tried this. I see that others have struggled to get this line to yield the desired results.
app.excelImportPreferences.tableFormatting = TableFormattingOptions.EXCEL_FORMATTED_TABLE
It seems to me that the only way I can set the desired formatting option is to do it manually in the UI, then run my script. The setting then sticks. But I don't have any control of the setting via script. If you know what I'm missing, please let me know. Thanks for the millionth time.
Paul
Copy link to clipboard
Copied
When I specify the range on some sheets, Indesign crashes
The question has to do with table formatting options. I tried this. I see that others have struggled to get this line to yield the desired results.
app.excelImportPreferences.tableFormatting = TableFormattingOptions.EXCEL_FORMATTED_TABLE
It seems to me that the only way I can set the desired formatting option is to do it manually in the UI, then run my script.
Hi Paul,
I made a quick test.
For example, let's place the first spreadsheet: a range -- from A3 to F8 -- as formatted table by script.

main();
function main() {
var textFrame = app.selection[0], // Select a text for pacing an Excel file
excelFile = new File("~/Desktop/Test/Test place Excel.xlsx");
if (textFrame.isValid && excelFile.exists) placeExcel(textFrame, excelFile);
}
function placeExcel(textFrame, excelFile) {
try {
with (app.excelImportPreferences) {
sheetIndex = 0;
tableFormatting = TableFormattingOptions.EXCEL_FORMATTED_TABLE;
rangeName = "A3:F8";
}
var placedStory = textFrame.place(excelFile);
}
catch(err) {
$.writeln(err.message + ", line: " + err.line);
}
}
The code works for me as expected in CC 2019 (Windows)

Regards,
Kasyan
Copy link to clipboard
Copied
Kasyan,
Yes, it works, but on some spreadsheets there are many problems. Often when I try to place spreadsheets manually, I will find an @ symbol in the select range field. When that is the case, those sheets won't place and indd crashes. The workaround seems to be adding or deleting a column to the sheet in question, saving the file, and then trying to place again. This works reliably. It is a known bug that is under review at indd. So now when I try to place spreadsheets, I wrap the place in a try and when it fails, I reopen the excel file, add columns to the sheet that failed, save, and retry the place. So far it has worked without fail.
On another closely related topic, I have found that only two of the four tableFormattingOptions are settable via script. EXCEL_UNFORMATTED_TABLE and UNFORMATTED_TABBED_TEXT work, but if you try to set EXCEL_FORMATTED_TABLE or FORMATTED_ONLY_ONCE, it won't work. Strangely, if you attempt to set FORMATTED, for example, and then you alert (excelImportPreferences.tableFormatting) it will say "EXCEL_FORMATTED_TABLE", yet if you then try to place, with the showing import options set to true, it will not work. So...
In my script I have another test for tableFormatting. If it is not FORMATTED, which is what I want, I alert the user that the following dialog requires that they set the formatting dropdown to formatted table. Then the script places a dummy file consisting of a single cell. If the user follows the instruction, indd formatting import options stays set to formatted until someone changes it.
Another known bug is that when placing xlsx files, you cannot see named ranges, meaning ranges that the author of the excel doc chooses for given ranges. It seems to me that the excel import options panel has not been sufficiently treated in the scripting language. Since indd is reviewing the @ symbol problem in the range selection, I hope they will also look over the other options as well.
Your help in all this has been essential. Unfortunately, I could not use your excellent getDataFromExcel script because I need the formatted text, but having learned how it works a bit, I was able to write all the workarounds I employed in AppleScript.
Also, possibly of interest to you, I have come up with an AppleScript that discovers the true used range in excel, which is another problem altogether. It turns out any cells in excel that have been used ever count as used cells. So in the case of the sheets I'm attempting to place, the ranges that indd finds are enormous though the real used range is only maybe 150 rows. So, via AppleScript I read the last cell that has a value in a given column and create a list of used ranges that feeds the placement of the various sheets.
Again, though I do things much less elegantly than you do, I am hitting the target, finally. Thanks again for all your support.
Paul
Copy link to clipboard
Copied
In case you're interested in testing the table formatting options problem, you'd have to set it to unformatted first. Then try setting the formatting to EXCEL_FORMATTED_TABLE. Then reopen the place with import options window and you should see that it says unformatted table still.
Copy link to clipboard
Copied
Thanks for your detailed explanation, Paul!
I just upgraded my Mac -- replaced hard drive -- so I am in the process of 'clean installing' latest OS and apps.
When I reinstall Script Debugger, I will experiment with what you said: your Actual Used Range vs the Excel's used range. Maybe later I will get back here and ask you some clarifying questions.
In case you're interested in testing the table formatting options problem, you'd have to set it to unformatted first. Then try setting the formatting to EXCEL_FORMATTED_TABLE. Then reopen the place with import options window and you should see that it says unformatted table still.
Now I tested it and see what you mean. However, In Data Browser the parameter is changed from unformatted to formatted, but the table, actually, is placed as unformatted. Definately it's a bug. I remember someone wrote about bugs on importing Excel here on the forum, but I can't find the post.
— Kas
Copy link to clipboard
Copied
Kasyan,
If you want wacky test excel documents that give reproduce the used range vs. real used range issue, let me know. I don't have any that produce the @ symbol anymore, but I bet I can get my client to provide one by accident.
Paul
Copy link to clipboard
Copied
If you want wacky test excel documents that give reproduce the used range vs. real used range issue, let me know.
Hi Paul,
Yes, I'd like to get some files to play with.
My e-mail: askoldich [at] yahoo [dot] com
— Kas
Get ready! An upgraded Adobe Community experience is coming in January.
Learn more