Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Get data directly from Excel

Valorous Hero ,
Feb 16, 2017 Feb 16, 2017

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:

  1. excelFilePath — 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";
  2. splitChar — [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.
  3. sheetNumber — [Optional] the worksheet number: either String or  Number. If it isn't set, the first worksheet will be used by default 

The data in Excel

The same data transfered to InDesign as Array

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

TOPICS
Scripting
15.6K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jan 31, 2019 Jan 31, 2019

Sorry to hear about the illness. I’m surely not helping. So, I’ve been using the Mac version with near success, but like I said no column or row separators and the file name doesn’t seem to work, though the script gets the data from whichever excel workbook is open. Get better soon! Maybe you can revisit my issue when you’re better. Thanks!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jan 31, 2019 Jan 31, 2019

Hi pjleveno ,

just tested Kasyan's version 2 script with an Excel file on Windows 10.

The function returns data, that is an array of arrays. Perhaps that was not clear.

So you cannot directly use it as contents for an InDesign table.

Here a representation of the data of an Excel file with only one worksheet and a data representation like that:

ExcelWorksheetData.PNG

The array's length is 5. Every entry represents a row in the worksheet.

So every entry of the 5 entries of the outer array is an array of 3 elements like that:

1,1,1

2,2,2

3,3,3

4,4,4

5,5,5

You could flatten the array of arrays to one array of string entries. Only then you could assign this array as contents for a table. In my reply above I was under the false impression that Kasyan's function returns an flat array. Totally my fault, because Kasyan made that very clear in his initial post.

Or, you do not flatten the result array and loop through the outer array to assign the inner arrays with the 3 entries as contents for rows of a table in InDesign.

Below a sample snippet where I pointed the script to an Excel file on drive F: and directed the entries of the result data as contents to the rows of an InDesign table in my open InDesign document:

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 = "F:\\Excel-TestFolder\\Columns-3-Cells-5.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

    var sheetNumber = "1";

    var data = GetDataFromExcelPC(excelFilePath, splitChar, sheetNumber); // returns array of arrays

//~    $.writeln( data.constructor.name );

//~    $.writeln( "data.length" +" : "+ data.length );

//~  

//~    $.writeln(data.join("\r"));

//~  

    var myInDesignTable = app.activeDocument.textFrames[0].tables[0];

    for( var n=0;n<data.length;n++ )

    {

        myInDesignTable.rows.contents = data;

    };

}

Screenshot of the result:

ExcelData-Transferred-to-InDesign-Table-RESULT.PNG

Sorry. Couldn't test the Mac OSX version code.

All my tests with Excel 2010 and InDesign CC 2019 version 14.0.1.209 on Windows 10 Pro.

Regards,
Uwe

//EDITED

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jan 31, 2019 Jan 31, 2019

Uwe,

Thanks! That's a relief that it was a particular problem of the Mac. So I have to use this. I was just doing a similar thing with xml, trying to point the contents from excel to cells in indd. It worked, though I do everything the slowest more laborious way possible. Thanks for helping yet again. I'll try your snippet and see if I can get the same result.

Paul

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jan 31, 2019 Jan 31, 2019

YOU NAILED IT! That is precisely what I wanted. Thanks to both of you for caring so much. I truly am grateful. Goodbye place(File)! forever.

Paul

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jan 31, 2019 Jan 31, 2019

Wow, all that work, and I just took the contents from the same workbook that originally prompted me to seek an alternative to place, and when I “get data” using your script, Kasyan, indd crashed, but only on that sheet, just like it was doing before. Are there characters in the text that could be causing this crash? The content includes equations because it refers to a math textbook. If your script can’t place it, and indd place can’t place it, well, that’s all there is.

Paul

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 01, 2019 Feb 01, 2019

pjleveno  wrote

…The content includes equations because it refers to a math textbook. If your script can’t place it, and indd place can’t place it, well, that’s all there is.

Hi Paul,

before placing you could "flatten" your Excel file and remove all equations, but keeping the results:

Delete or remove a formula - Excel

Regards,
Uwe

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Feb 01, 2019 Feb 01, 2019

Thanks again for taking an interest. I am now rolling and with your help, this looks great. I still haven’t figured out how to pass a file into the function properly. When I simply write it out with colons it works, but when I try to pass the file in as a variable, it doesn’t work. I’m on a Mac. Is there something I have to do to the file variable so that it works? Really, this is way beyond what I thought I was going to get out of this script. It has saved me! Thanks!

Paul

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Feb 02, 2019 Feb 02, 2019

I still haven’t figured out how to pass a file into the function properly. When I simply write it out with colons it works, but when I try to pass the file in as a variable, it doesn’t work. I’m on a Mac.

I can't get to my Mac now, but here a couple of examples:

Here Macintosh HD is a local hard drive

var excelFilePath = "Macintosh HD:Ecclesall Print:Christmas card project:Schools:Ecclesall Junior School:Ecclesall Junior School.xlsx";

Here Test is a network share

var excelFilePath = "Test:My Folder:SampleBook.xlsx";

Here's a snippet from a script of mine (the beginning of a function) to give you idea how to 'convert' JS-path.

function NormalizeData(schoolFolder) {

    var spread, classSpread, row,

    book = [],

    splitChar = ";";

   

    var excelFiles = schoolFolder.getFiles("*.xlsx");

   

    if (excelFiles.length == 0) {

        return null;

    }

    var excelFile = excelFiles[0]; // get the 1st excel file: the name doesn't matter

   

    if (File.fs == "Windows") {

        var excelFilePath = excelFile.fsName.replace(/\\/g, "\\\\");

    }

    else {

        var excelFilePath = excelFile.fullName.replace(/^\//g, "").replace(/\//g, ":");

    }

    // Get  data from Excel

    try {

        if (File.fs == "Windows") {

            var firstSpread = GetDataFromExcelPC(excelFilePath, splitChar, 1);

        }

        else {

            var firstSpread = GetDataFromExcelMac(excelFilePath, "|", splitChar, 1);

        }

       

By the way, my script works with a workbook who has formulas. My client sends to customers a blank copy of Excel file and they fill in the order: names, how many of which products to print, etc. Excel calculates the cost on the fly and the file is used to create the required products: PDF-files ready to print. Everything is processed automatically.

— Kas

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Feb 02, 2019 Feb 02, 2019

Kasyan,

Perfect! I had to add replace(/^~/g, "Macintosh HD:Users:me”)

But that was the move. Your script is fantastic. I hope to learn how to use it in other cases. I can imagine many great uses. Absolutely perfect for the case I’m on now, but surely if I ever deal with excel in the future, I’ll be using this. Not having to declare range names which required save as xls, has saved me all sorts of problems. Now I can leave you in peace!

Paul

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Feb 03, 2019 Feb 03, 2019
I had to add replace(/^~/g, "Macintosh HD:Users:me”)

I am glad to hear you finally sorted it out on your own. Though I don't understand why would you want to globally replace a tilda at the beginning of the string with a Mac specific path.

— Kas

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Feb 03, 2019 Feb 03, 2019

Kasyan,

I don’t suppose the “g” is necessary. I’m just doing trial and error. Of course the worst way.

For example, I was using a pipe as row separator and semicolon as column separator, and everything was fine except when there is a semicolon in the content of a cell on the table I’m trying to place. Can I put anything I like as a column separator to avoid confusion? Could column and row separator character be multiple characters? I replaced the semicolon with a dollar sign and it worked…until the text is about dollars, I’m sure.

Thanks again!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Feb 03, 2019 Feb 03, 2019

Kasyan,

Answered my own question. So, unless you tell me otherwise, it seems like there's no downside to creating column and row separators that are very unlikely to appear in the content. On a side note, I was exploring your "File" scripts. Very exciting!


Paul

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Feb 03, 2019 Feb 03, 2019

it seems like there's no downside to creating column and row separators that are very unlikely to appear in the content.

That’s right. I am sure, in most cases, the default separators -- pile and semicolon -- would work without any issues. If they are used somewhere in text, you can use some other chars instead.

— Kas

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 09, 2019 Jul 09, 2019

I have noticed that some characters script read as code instead actual character, for example:

right single quotation mark - \u2019

left double quotation mark - \u201C

right double quotation mark - \u201D

How we can change code to give actual character instead of code?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Jul 10, 2019 Jul 10, 2019

I just tested it (Windows 10, InDesign 14.0.2, Excel 2019 (16.0.11727.20222)), but this doesn't happen on my side.

Spreadsheet

2019-07-10_10-06-57.png

Array in data browser

2019-07-10_10-07-49.png

The same array converted  to string and placed as contents into a text frame by script

2019-07-10_10-08-41.png

I have a superficial knowledge of Excel and have no idea why it works differently for you. Probably you have to play with some settings.

— Kas

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 10, 2019 Jul 10, 2019

Probably i will try to solve this with native js function decodeURIComponent().

Thank you for this code.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Jul 10, 2019 Jul 10, 2019
LATEST

Probably you won't need it.

For example (assuming a text frame is selected):

main();

function main() {

    var textFrame = app.selection[0];

    var str = "left single quotation mark - \u2018\nright single quotation mark - \u2019\nleft double quotation mark - \u201C\nright double quotation mark - \u201D";

    textFrame.contents = str;

}

I get quotes as expected.

2019-07-10_14-17-55.png

Also, in my opinion, these encode/decode functions have a different purpose: encode/decode special chars in URI.

Anyway, taking pairs of single and double quotation marks as an example, in ESTK they work like so:

2019-07-10_14-21-16.png

Not what you expect to get (I guess).

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Feb 21, 2017 Feb 21, 2017

Uwe, thanks, the penny finally dropped, TableFormattingOptions.EXCEL_FORMATTED_TABLE did the trick with my -1 problem.

Loic, I am going to look into a C++ solution.

P.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 21, 2017 Feb 21, 2017

Pickory  wrote

Uwe, thanks, the penny finally dropped, TableFormattingOptions.EXCEL_FORMATTED_TABLE did the trick with my -1 problem …

Hi Pickory,

aha!

I guess it is all cells formated as text with Excel and place unformatted,

e.g. as EXCEL_UNFORMATTED_TABBED_TEXT

vs.

using EXCEL_FORMATTED_TABLE with the options

if the cells are not formatted as text.

Regards,
Uwe

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Feb 19, 2017 Feb 19, 2017

What a coincidence, I was just about to share my little script that also touches the topic of using Excel data without going the long route through a txt file. Sometimes, I only need some small table data copied over from Excel into my library or selected ID table. I came up with the idea to copy some cells from Excel into the clipboard and then just run the script. It places the clipboard into a temp frame and moves it into an array.

Notes:

  • Replaced the typographer's apostrophe before moving it to the array, so that the typo settings don't matter. Apostrophes can cause a problem when the data is supposed to link to an image later.
  • Not sure if there is a more elegant solution to get rid of the paragraph mark

I have been using it quite a bit and it works flawless. Any suggestions how to improve it? One from me: The new text frame is always placed at 0,0 so when I'm zoomed in on the righthand side page, the script jumps to that 0,0 location, which is a bit annoying. Could create the frame next to a selected object or active page.

function ClipboardToArray() {
     var myFrame = app.activeWindow.activePage.textFrames.add(undefined, undefined, undefined, {geometricBounds:[0,0,100,100]}),

     myArray = [];

     with (myFrame.textFramePreferences) {

          autoSizingType = AutoSizingTypeEnum.HEIGHT_AND_WIDTH;

          autoSizingReferencePoint = AutoSizingReferenceEnum.TOP_LEFT_POINT;

          useNoLineBreaksForAutoSizing = true;

     }

     myFrame.insertionPoints[0].select(); app.paste();

     app.findGrepPreferences = app.changeGrepPreferences = null;

     app.findGrepPreferences.findWhat = "'";

     app.changeGrepPreferences.changeTo = "~'";

     myFrame.changeGrep();

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

          var myLine = myFrame.paragraphs.contents;

          if (i < myFrame.paragraphs.length -1) {

               myLine = myLine.substring(0, myLine.length -1);

          }

          myLine = myLine.split("\t");

          myArray.push(myLine);

     }

     myFrame.remove();

     return myArray;

}

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Feb 19, 2017 Feb 19, 2017

Oh, and sorry for my ignorance, how do you get the nice background when entering code? Just <code> in HTML mode?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 20, 2017 Feb 20, 2017

AnotherFrank  wrote

Oh, and sorry for my ignorance, how do you get the nice background when entering code? Just <code> in HTML mode?

Hi Frank,

log in to the forum and click:

Use advanced editor

Some new formatting features should be available now.

Select your text you want to format as code.

Go to >> Syntax Highlighting > javascript

( you have to scroll a bit down to see this option )

Unfortunately enable Use advanced editor is no personal preference with the forum software so you have to click it every time you reply.

Important: With Inbox, Use advanced editor is not available.

Regards,
Uwe

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Feb 20, 2017 Feb 20, 2017

Thanks, Uwe!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Feb 20, 2017 Feb 20, 2017

There might be dll/framework solutions for sure. I have been told one but it's a commercial one and it's like 1.3k € to distribute it. So it's quite expensive if it's for selling a script 500€

It seems python has some utilities too but it implies python being installed.

I wish I knew C++

Loic

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jan 31, 2019 Jan 31, 2019

Thanks! I feel like I'm so close to understanding, yet I don't see how I get "data" outside the function. What am I doing wrong?

Main();

app.activeDocument.textFrames[0].tables[0].contents=data;

function Main() {

returns "data is undefined"

How do I refer to "data" outside the function? I am sure I am missing some basic javascripting skills, so please be patient with me. You guys are great, by the way. I try to learn from your scripts all the time. Thanks again!

Paul

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines