Tuesday, February 17, 2009

Convert Word 2007 Documents and Upload to Google Docs

It started with a script by Michael Suodenjoki called "docx2pdf.vbs" that lets you convert individual Word 2007 documents to PDF. It is possible to use it to process a batch of files, such as all documents in a specific directory, by using (programming) another sophisticated MS DOS batch script, but that is a pain in the neck itself. I ran into issues, because I had spaces in the file names of the documents that I wanted to convert and I thought to myself that it is probably easier and safer to change the VBScript itself instead of writing another script in a different language.

By the way, for the conversion to PDF format with Microsoft Word 2007 is it necessary that you installed next to Microsoft Office 2007 of course, also the "Microsoft Save as PDF" Add-In for 2007 Office, which is available for free download at the Microsoft.com web site here.

Extending the Docx2Pdf Conversion Script

So I changed the script to allow the processing of a whole folder instead of just one file. While I was doing that, I realized that I need  a filter to make sure that I only process  documents that Word 2007 can load. While I was thinking about a solution about that, I realized that a)  Word 2007 supports more than just Word 2007 .DOCX documents and b) that the Google Docs API only supports Word 97 .DOC documents and not PDF, like the manual uploader in their web interface. Converting my Word 2007 documents to be able to get them up on Google Docs was actually the cause to get me started with all this.

Google Docs Limitations

The Google Docs web uploader supports Adobe PDF and Word 97 DOC format for documents, but you have to upload documents one by one manually. That sucks big time and I hope that Google will do something about that soon, but in the mean time there is not much we can do about it. In order to get several documents up on Google Docs quickly, you have to use their API or the create document via Email or Email with Word 97 document attachment features. Adobe PDF is supposed to work with their GData API for Google Docs, but I never got those Python scripts to work. I was able to connect to Google, but not to transfer... anyway.

Word 2007 supports the output of documents in numerous formats, including Word 97, HTML, RTS, TXT and PDF, if you installed the free Microsoft PDF Add-on. So I added the option to specify the output format.

Then I was thinking of automation of uploads to Google Docs and realized that I needed an additional filter to avoid that I will ending up sending the same documents to Google over and over again. Thus I added some date filter options based on the creation date and date when the document was last modified.

Last but not least I had to add the option to send the converted documents (that were converted to .DOC format) to Google Docs. The upload via email was the way to go and my script was complete. Here is what came out of all of this.

Save Word 2007 Documents in a different Format and more

Usage: cscript /nologo doc2pdf.vbs [<input-file>] [/dir:<input-dir>] 
[/date:created/modified] [/datefrom:mm-dd-yyyyy] [/dateto:mm-dd-yyyy] [/days:nn]
[/f:<output-format>] [/o:<output-file>] [/googledocs:<email>]

Basic Options:


/help  
Specifies that this usage/help information should be displayed.


/verbose
Specifies that detailed information about the processing should be displayed.


Parameters (optional):

/dir:<input-dir>
Directory Batch Processing. Use <input-file> argument as filter option (e.g. *.DOCX or *.* for all)
Date Filter Options

/date:created/modified
Date filter option (only for batch processing). Specify if the date parameters should apply to either the
creation date of the file or the date when it was last modified.


/datefrom:mm-dd-yyy
/datetto:mm-dd-yyy

From and To Date options for that work together with the /date option. If /dateto is not provided, then the script will automatically assume today. Alternative to a specific date range is it possible to provide the following parameter


/days:nn
"nn" stands for a numeric value from 0 to whatever and are the number of days from today, to determine the from date. The value 1 for example would mean yesterday, or files that were created/changed since yesterday until today. 7 would be a week and so on.


Output Options Parameters


/f:<format>
Specifies the output format values: DOCX, DOCM, DOTX, DOTM, XML, PDF (default), XPS, TXT, DOS, ASC, HTML, HTM, RTS, DOC, WEB


/o:<file>
Optionally specification of output file, which only works for single file conversions and not batch processing.


Send to Google Docs via Email Option


/googledocs:<email>


Your personal email to upload documents, which looks like: longstring@prod.writely.com
You can find that email, if you login to your Google Docs account and go to the "Upload" screen.


Note: Google Docs only supports the import of documents in DOC file format (Office 97).
PDF files are not supported. The file size is also limited to 500KB per document.


Examples:


1. Convert all Word 2007 documents (.docx) that were modified on or since December 31, 2008 and are located in C:\DOCS to PDF and show detailed information about the processing.

cscript /nologo docx2pdf2.vbs *.docx /f:PDF /dir:C:\DOCS /date:modified /datefrom:12-31-2008 /verbose

2. Convert all Word 2007 documents (.docx) that where created in the last 7 days and are located in
C:\MY DOCUMENTS to Word 97 (.DOC) and send them to Google Docs.

cscript /nologo docx2pdf2.vbs *.docx /f:DOC /dir:"C:\MY DOCUMENTS" /date:created /days:7 
/googledocs:mysecretemail@prod.writely.com

3. Convert a single Word 2007 Document C:\DOCS\mydocument.docx to PDF and save it as
C:\MY DOCUMENTS\mynewdocument.pdf

cscript /nologo docx2pdf2.vbs "C:\DOCS\mydocument.docx" /o:"C:\MY DOCUMENTS\mynewdocument.pdf" 

4. Convert C:\DOCS\mydocument.docx to HTML and save it in the same folder

cscript /nologo docx2pdf2.vbs "C:\DOCS\mydocument.docx" /f:HTML 


Notes and Considerations


The script must be called with CSCRIPT.EXE instead of WSCRIPT.EXE, because it makes use of the STDOUT option of VBSCRIPT. WSCRIPT.EXE is used by default, if you start a .VBS Visual Script document (e.g. double click or call from a .BAT file).  Its not the end of the world, if you dont, because the script will re-launch itself 
using CSCRIPT, if you execute it with WSCRIPT anyway. However that might cause other  issues, depending how you are using this script.  For example the redirection of the output into a text file wont work, because 
the script opens a new DOS session. I used the proper call of the script in my examples above. The "/nologo" parameter is not required by the way. I only use it to suppress the Microsoft messages and screens.


There are different ways to send out emails via a script that runs on a users computer, each with their own problems and advantages. I implemented two options into my script. By default is Microsofts CDO used to send out the emails to Google Docs, but I also added the option to use Outlook MAPI instead.


Using MAPI instead of CDO


Pretty near the beginning of the script is a line that reads:

Const EmailSendMethod = 2 1 = MAPI (Outlook), 2 = CDO

To use MAPI instead of CDO, simply change this line to

Const EmailSendMethod = 1 1 = MAPI (Outlook), 2 = CDO

I wanted to go with MAPI first, but the biggest problems that I had were those stupid security messages that popped up every time that I wanted to send one email to Google. Since I do not want to sit in front of the computer to acknowledge dozens or more emails/documents to be sent, I changed the script to use CDO instead.


DOCX2PDF-MAPI-Security1Here is what I got when I used MAPI. First was this message, which made perfect sense to me, although I missed the option to say "always allow access for this application" that it does not come up in the future anymore.


I know that from Skype and it works perfect there, but nothing like it in Microsoft Outlook.


 


Anyhow, I set the option to "Allow access for 10 minutes", which should have been enough to process a bunch of documents and send them out to Google, but it wasnt to be like that. Instead I got a pop-up every time I created a new Email object. The Message window has the "Allow" button disabled for several seconds, before you can approve the email to be created and nothing is done until you pressed "Allow", over and over again.


DOCX2PDF-MAPI-Security2


If you know how I can tell Outlook 2007 to trust my script that resides locally on the users machine and thus be able to use MAPI to generate email messages automatically, please let me know. I know that you can get something like a "Trusted Publisher Certificate", but hey, I am not a software company who is selling a commercial plug-in to people, not even a compiled program at all. Its plain and pure code that is executed and processed by Windows on the fly, without the need to compile it first.


CDO Requirements and Troubleshooting


CDO uses the build-in SMTP server feature that comes with Windows XP (except XP Home). It is not installed by default. You can install SMTP by going to "Start\Control Panel\Add or Remove Programs". There select on the left the Tab "Add/Remove Windows Components". Scroll down to "Internet Information Services (IIS)", highlight it and press the "Details" button. There you have to enable "SMTP Service", which will automatically also enable "Common Files" and "World Wide Web Service", if it was not installed already on your computer either. Press "Okay" to close the "Details" screen and then "Next". Follow the steps on the screen. You might require your original Windows Installation Disk, so have it handy before you get started with this.


The script will error out, if you do not have SMTP installed on your machine yet!


The nice thing about it is that you dont even require to have an email client installed and running (or ready to run) in order to send out an email from your machine. That is a draw-back as well, because you will dont have a reference or copy of the emails that you sent out. They wont appear in  the "Sent" folder of MS Outlook, Outlook Express, Live Mail or whatever email client you might be using.


DOCX2PDF-CDO-PickupFolder


If you had SMTP installed on your computer already, but not running (like I did), then the script will run without any errors. It will also say that it sent out the emails to Google, but that is not entirely correct. It created it, yes, but sending them is an entirely different story. If the SMTP service is stopped, no email is being sent. Windows dumps the prepared emails into the "Pickup" folder of "IIS", which is located by default at "C:\Inetpub\mailroot\Pickup", unless you configured it to be somewhere else.


Make sure that the SMTP Service is running. To check that and to start the service manually, go to your computers "Administrative Tools" and start "Services" (Administrative Tools are located in the "Control Panel", like the "Add or Remove Programs" tool). Look for the service with the name "Simple Mail Transfer Protocol (SMTP)". See the image below for illustration.


DOCX2PDF-CDO-SMTP-Service


Double-Click on it and press "Start", if the status of the service is anything else than "Started". If it fails to start, then it could be that services where it depends on are not started as well. You can find out by selecting the "Dependencies" tab. Make sure that the services listed there are running as well.


DOCX2PDF-CDO-SMTP-Dependency


See on the left. It shows that the "Event Log" and "IIS Admin" services need to run that the SMTP service can be started as well.


Once the SMTP service is running, you are ready to rock and roll. If you ran the script and then found out that the service was not running, emails stuck in the "Pickup" folder will not be processed automatically once SMTP is running. Another email must be triggered that SMTP will pick them up and add them to its processing "Queue" folder for sending and then finally send it out to Google.


 


DOCX2PDF-CDO-Queue


SMTP Service Security (for CDO)DOCX2PDF-CDO-IIS-SMTP1


Having the SMTP Service running will upset some anti-virus or anti-spyware programs, because it can become an issue, if it is not secured, detected by hackers and then abused to relay spam message via your computer to other people on the Internet.


You dont to invite trouble, so go and change the security settings that nobody else can use your mail service, except you.


DOCX2PDF-CDO-IIS-SMTP2Open the IIS manager, which is located under "Administrative Tools" (like the "Services" tool). There you will find the item "Default SMTP Virtual Service" entry on the left.


Highlight it, right-click with your mouse and select "Properties". Then select the "Access" tab and click on the "Authentication" button.


It is probably set to "Anonymous Access". Change that to "Integrated Windows Authentication".


Users that need permissions to use the service can be added via the "Security" tab as needed.


This will take of things and keep your SMTP service on your machine secure.


Email Delivery Issues with Google Docs


I use the secret email address for your Google Docs account not only as the TO address of the email, but also as the sender address as well. This has a nice little side effect to it.


Sometimes there are issues with Google Docs and it is unable to process a document that it received via attachment of an email for whatever reason.


If the email delivery fails, a system notification is being sent by the receiving mail server to the sender of the email, your Google Docs upload email address. Since the email upload feature also supports sending a whole text email to be converted to a document, this is what Google Docs is doing with the Error emails that it receives about the original uploads that failed.DOCX2PDF-CDO-GoogleDocs1


If you open up such Error Email document, you will be able to find out, which document (file name) that was sent by the script was not uploaded and processed properly. Just send it again with another batch or upload it manually via the web interface, whatever is easier for you.


 DOCX2PDF-CDO-GoogleDocs2


The Script and the Source Code Download


Here is the source code that you can see how the various features were implemented (and to use it for your own tools and scripts hehe). Dont type it off the screen or copy and paste it into a document, if you want to use my script as it is. You can download the full source of it here as a text file. It is called docx2pdf2.vbs.txt. Rename it to remove the .TXT and you are ready to rock and roll.


Thats it! Have Fun!


Carsten aka Roy/SAC


   1:  
   2:   DOC2PDF2.VBS Microsoft Scripting Host Script (Requires Version 5.6 or newer)
   3:   --------------------------------------------------------------------------------
   4:   Author: Carsten Cumbrowski 
   5:   Created: December 2008 and February 2009
   6:   
   7:   Based on a script by  Michael Suodenjoki
   8:   This script can create a PDF file from a Word document provided youre using
   9:   Word 2007 and have the Office Add-in: Save As PDF installed.
  10:   It can also save the documents in other formats that are supported by MS Word 2007
  11:   The script allows batch processing of multiple files (e.g. a directory) and also the 
  12:   sending via email to Google Docs (output format DOC, Office 97 required) 
  13:  
  14:   Script must be called with CSCRIPT instead of WSCRIPT
  15:   Its not the end of the world, if you dont, because the script will relaunch itself 
  16:   using CSCRIPT, if you execute it with WSCRIPT anyway. However that might cause other
  17:   issues, depending how you are using this script
  18:   For example the redirection of the output into a text file wont work, because 
  19:   the script opens a new DOS session
  20:   
  21:  ForceCscript true
  22:   Constants
  23:   
  24:  Const iGoogleDocsSizeLimit = 5120000
  25:  Const iGoogleDocsReqFormat = 0
  26:   
  27:  Const WdDoNotSaveChanges = 0
  28:  Const EmailSendMethod = 2   1 = MAPI (Outlook), 2 = CDO
  29:   
  30:   see WdSaveFormat enumeration constants: 
  31:   http://msdn2.microsoft.com/en-us/library/bb238158.aspx
  32:  Const wdFormatPDF = 17   PDF format. 
  33:  Const wdFormatXPS = 18   XPS format. 
  34:  Const wdFormatDocument97=0  Word97 Format
  35:  Const wdFormatDocumentDefault = 16 Word Default (.DOCX in Word2007)
  36:   
  37:  Const wdFormatWebArchive = 9 Web Archive
  38:   
  39:  Const wdFormatTemplate = 1 Word 97 Template
  40:   
  41:  Const wdFormatRTF = 6 RTF
  42:   
  43:  Const wdFormatHTML = 8 Std HTML
  44:  Const wdFormatFilteredHTML = 10 Filtered HTML
  45:   
  46:  Const wdFormatText = 2 Windows Text
  47:  Const wdFormatTextLineBreaks = 3 Windows Text with line-breaks preserved
  48:  Const wdFormatEncodedText = 7 Encoded Text (Unicode)
  49:  Const wdFormatUnicodeText = 7
  50:  Const wdFormatDOSTextLineBreaks = 5 DOS Text w Line-Breaks
  51:  Const wdFormatDOSText = 4 DOS
  52:   
  53:   
  54:  Const wdFormatXML = 11 XML
  55:  Const wdFormatXMLDocument = 12 XML DOc   .docx
  56:  Const wdFormatXMLDocumentMacroEnabled = 13 XML Macros Enabled  .docm
  57:  Const wdFormatXMLTemplate = 14 XML Template .dotx
  58:  Const wdFormatXMLTemplateMacroEnabled = 15 XML Template Macros Enabled  .dotm
  59:   
  60:  Const TEXTMSG = 0
  61:  Const HTMLMSG = 1
  62:   
  63:   Global variables
  64:  Dim arguments, bShowDebug, sOutFormat, iwdFormat, sOutExt, sFolder
  65:  Dim owdo  As Word.Application
  66:  Dim owdocs  As Word.Documents
  67:  Dim ofso, bDateFilter, sDateFilterType, sDateFrom, sDateTo, iDays
  68:  Dim bVerbose,output, sGoogleEmail
  69:  Dim ol, ns
  70:   
  71:  Set arguments = WScript.Arguments
  72:  sOutFormat = "PDF"
  73:  bDateFilter = false
  74:   
  75:  sDateFilterType = ""
  76:  sDateFrom = ""
  77:  sDateTo = ""
  78:  sGoogleEmail = ""
  79:   
  80:   
  81:  --------------------------------------------------------------------------------------------
  82:  Function DetermineOutFormat(sOut)
  83:     Dim iOut
  84:     Select Case ucase(sOUt)
  85:      Case ""
  86:          iOut = wdFormatPDF 
  87:          sOutExt = "pdf"
  88:    Case "PDF"
  89:          iOut = wdFormatPDF 
  90:    Case "XPS"
  91:          iOut = wdFormatXPS 
  92:    Case "DOC"
  93:          iOut = wdFormatDocument97
  94:      Case "DOT"
  95:          iOUt = wdFormatTemplate 
  96:    Case "HTML"
  97:          iOut = wdFormatHTML 
  98:    Case "HTM"
  99:          iOut = wdFormatFilteredHTML 
 100:    Case "DOCX"
 101:          iOut = wdFormatXMLDocument 
 102:    Case "DOCM"
 103:          iOut = wdFormatXMLDocumentMacroEnabled 
 104:    Case "DOTX"
 105:          iOut = wdFormatXMLTemplate 
 106:    Case "DOTM"
 107:          iOUt = wdFormatXMLTemplateMacroEnabled 
 108:    Case "TXT"
 109:          iOut = wdFormatTextLineBreaks 
 110:    Case "XML"
 111:      iOUt = wdFormatXML 
 112:    Case "ASC"
 113:          iOUt = wdFormatDOSTextLineBreaks 
 114:    Case "DOS"
 115:          iOUt = wdFormatDOSText 
 116:          sOutExt = "txt"
 117:      Case "RTF"
 118:          iOut =     wdFormatRTF     
 119:      Case "WEB"
 120:          iOUt = wdFormatWebArchive 
 121:      Case else
 122:         iOut = -1
 123:    End Select
 124:    DetermineOutFormat = iOut
 125:   
 126:  End Function
 127:   
 128:   
 129:  ---------------------------------------------------------------------------------------------
 130:   ECHOUSAGE
 131:   Outputs the usage information.
 132:  
 133:  Function EchoUsage()
 134:    If arguments.Count=0 Or arguments.Named.Exists("help") Or _
arguments.Named.Exists("h") or iwdFormat=-1 Then
 135:     output.WriteLine "Save Word 2007 Compatible Document(s) in a different Format and more"
 136:     output.WriteLine ""
 137:     output.WriteLine "Usage: cscript /nologo doc2pdf.vbs [<input-file>] [/dir:<input-dir>] " & _ 
"[/date:created/modified] [/datefrom:mm-dd-yyyyy] " & _
"[/dateto:mm-dd-yyyy] [/days:nn] [/f:<output-format>] " & _
"[/o:<output-file>] [/googledocs:<email>]"
 138:     output.WriteLine  ""
 139:     output.WriteLine  "Available Options:"
 140:     output.WriteLine  "=================="
 141:     output.WriteLine  "/help - Specifies that this usage/help information should be displayed."
 142:     output.WriteLine  "/verbose - Specifies that detailed information about the " & _
"processing should be displayed."
 143:     output.WriteLine  ""
 144:     output.WriteLine  "Parameters (optional):"
 145:     output.WriteLine  "======================"
 146:     output.WriteLine  "/dir:<input-dir>"
 147:     output.WriteLine  "    Directory Batch Processing. Use <input-file> argument as " & _
"filter option (e.g. *.DOCX or *.* for all)"
 148:     output.WriteLine  ""
 149:     output.WriteLine  "Date Filter Options"
 150:     output.WriteLine  "--------------------"
 151:     output.WriteLine "/date:created/modified - Date filter option"
 152:     output.WriteLine "/datefrom:mm-dd-yyy - From Date for date-created or date-modfied filter"
 153:     output.WriteLine "/datetto:mm-dd-yyy - To Date for date-created or date-modfied filter"
 154:     output.WriteLine " ...or .."
 155:     output.WriteLine "/days:nn - created/changed within the last nn days. "
 156:     output.WriteLine ""
 157:     output.WriteLine "Output Options Parameters"
 158:     output.WriteLine "-------------------------"
 159:     output.WriteLine  " /f:<format> Specifies the output format values:"     
 160:     output.WriteLine  "    DOCX, DOCM, DOTX, DOTM, XML, PDF (default), XPS, TXT," & _
" DOS, ASC, HTML, HTM, RTS, DOC, WEB"
 161:     output.WriteLine  " /o:<file> Optionally specification of output file."
 162:     output.WriteLine ""
 163:     output.WriteLine "Send to Google Docs via Email Option"
 164:     output.WriteLine "------------------------------------"
 165:     output.WriteLine "Note: Google Docs only supports the import of documents in " & _
"DOC file format (Office 97)."
 166:     output.WriteLine "PDF files are not supported. The file size is also limited to " & _
"500KB per document."
 167:     output.WriteLine ""
 168:     output.WriteLine "/googledocs:<email> - your personal email to upload documents," & _
                             " which looks like: longstring@prod.writely.com "
 169:     output.WriteLine ""      
 170:     output.WriteLine  "Examples:"
 171:     output.WriteLine  "========="
 172:     output.WriteLine "1. Convert all Word 2007 documents (.docx) that were modified " & _
"on or since December 31, 2008 and are located in C:\DOCS " & _
"to PDF and show detailed information about the processing."
 173:     output.WriteLine ""
 174:     output.WriteLine "cscript /nologo docx2pdf2.vbs *.docx /f:PDF /dir:C:\DOCS " & _
"/date:modified /datefrom:12-31-2008 /verbose"
 175:     output.WriteLine ""
 176:     output.WriteLine "2. Convert all Word 2007 documents (.docx) that where created " & _
"in the last 7 days and are located in C:\MY DOCUMENTS to " & _
"Word 97 (.DOC) and send them to Google Docs. "
 177:     output.WriteLine ""
 178:     output.WriteLine "cscript /nologo docx2pdf2.vbs *.docx /f:DOC /dir:""C:\MY DOCUMENTS"" " & _
"/date:created /days:7 /googledocs:mysecretemail@prod.writely.com"
 179:     output.WriteLine ""
 180:     output.WriteLine "3. Convert a single Word 2007 Document C:\DOCS\mydocument.docx " & _
"to PDF and save it as C:\MY DOCUMENTS\mynewdocument.pdf"
 181:    output.WriteLine ""
 182:     output.WriteLine "cscript /nologo docx2pdf2.vbs ""C:\DOCS\mydocument.docx"" " & _
"/o:"
"C:\MY DOCUMENTS\mynewdocument.pdf"""
 183:     output.WriteLine ""
 184:     output.WriteLine "4. Convert C:\DOCS\mydocument.docx to HTML and save it in " & _
"the same folder"
 185:     output.WriteLine ""
 186:     output.WriteLine "cscript /nologo docx2pdf2.vbs ""C:\DOCS\mydocument.docx"" /f:HTML"
 187:     output.WriteLine ""                         
 188:    End If 
 189:  End Function
 190:   
 191:  -------------------------------------------------------------------------------------------
 192:   CHECKARGS
 193:   Makes some preliminary checks of the arguments.
 194:   Quits the application is any problem is found.
 195:  
 196:  Function CheckArgs()
 197:     Check that <doc-file> is specified
 198:    If arguments.Unnamed.Count <> 1 Then
 199:      output.WriteLine "Error: Obligatory <doc-file> parameter missing!"
 200:      WScript.Quit 1
 201:    End If
 202:   
 203:  if arguments.Named.Exists("f") then
 204:        sOutFormat = arguments.Named.Item("f")
 205:  end if
 206:   
 207:  if arguments.Named.Exists("googledocs") then
 208:    sGoogleEmail =  arguments.Named.Item("googledocs")
 209:  end if    
 210:  if sGoogleEmail <> "" then
 211:     sOutFormat = "DOC"
 212:  end if
 213:   
 214:  sOutExt = sOutFormat
 215:  iwdFormat = DetermineOutFormat(sOutFormat)
 216:   
 217:  if arguments.Named.Exists("date") then
 218:   
 219:      if arguments.Named.Exists("days") then
 220:         if isNumeric(arguments.Named.Item("days")) then
 221:            iDays = int(abs(arguments.Named.Item("days")))
 222:            sDateFrom = formatdatetime(dateadd("d",iDays*-1,now),vbshortdate)
 223:            sDateTo = formatdatetime(now,vbshortdate)
 224:         end if
 225:       else
 226:          if arguments.Named.Exists("datefrom") then
 227:            if isDate(arguments.Named.Item("datefrom")) then
 228:              sDateFrom = formatdatetime(arguments.Named.Item("datefrom"),vbshortdate)
 229:            end if
 230:          end if
 231:           if arguments.Named.Exists("dateto") then
 232:             if isDate(arguments.Named.Item("dateto")) then
 233:                sDateTo = formatdatetime(arguments.Named.Item("dateto"),vbshortdate)
 234:             end if
 235:           else
 236:               sDateTo = formatdatetime(now,vbshortdate)
 237:           end if
 238:           end if
 239:           
 240:       if arguments.Named.Item("date") = "created" then
 241:          sDateFilterType = "c"
 242:       end if
 243:       if arguments.Named.Item("date") = "modified" then
 244:          sDateFilterType = "m"
 245:       end if
 246:      
 247:       if sDateFilterType <> "" and sDateFrom <> "" and sDateTo <> "" then
 248:          bDateFilter = true
 249:                  if bVerbose = true then
 250:                     output.WriteLine "Date Filter on! From Date: " & sDateFrom & _
", To Date: " & sDateTo & ", Filter: " & sDateFilterType
 251:                  end if
 252:       end if
 253:   
 254:  end if
 255:   
 256:   
 257:   
 258:   
 259:  End Function
 260:   
 261:  ------------------------------------------------------------------------------------------
 262:   DOC2PDF
 263:   Converts a Word document to PDF using Word 2007.
 264:   Input:
 265:   sDocFile - Full path to Word document.
 266:   sPDFFile - Optional full path to output file.
 267:  
 268:   If not specified the output PDF file
 269:   will be the same as the sDocFile except
 270:   file extension will be .pdf.
 271:  
 272:  Function DOC2PDF( sDocFile, sPDFFile )
 273:   
 274:   
 275:    Dim owdoc  As Word.Document
 276:    Dim sPrevPrinter  As String
 277:   
 278:   
 279:    if sFolder = "" then
 280:        sDocFile = ofso.GetAbsolutePathName(sDocFile)
 281:          sTmpFolder = ofso.GetParentFolderName(sDocFile)
 282:    else
 283:      sDocFile = sDocFile
 284:      sTmpFolder = sFolder
 285:    end if
 286:   
 287:    If Len(sPDFFile)=0 Then
 288:      sPDFFile = ofso.GetBaseName(sDocFile) + "." + lcase(sOutExt)
 289:    End If
 290:   
 291:    If Len(ofso.GetParentFolderName(sPDFFile))=0 Then
 292:      sPDFFile = sTmpFolder + "\" + sPDFFile
 293:    End If
 294:   
 295:   
 296:     Enable this line if you want to disable autoexecute macros
 297:     owdo.WordBasic.DisableAutoMacros
 298:       if bVerbose = true then
 299:           output.WriteLine "Converting: " & sDocFile 
 300:       end if                
 301:     Open the Word document
 302:    Set owdoc = owdocs.Open(sDocFile)
 303:   
 304:     Let Word document save as PDF
 305:     - for documentation of SaveAs() method,
 306:       see http://msdn2.microsoft.com/en-us/library/bb221597.aspx 
 307:    owdoc.SaveAs sPDFFile, iwdFormat 
 308:   
 309:    owdoc.Close WdDoNotSaveChanges
 310:   
 311:       if bVerbose = true then
 312:           output.WriteLine "Saved As: " & sPDFFile & "(Format: " & iwdFormat & ")"
 313:       end if                
 314:   
 315:       if sGoogleEmail <> "" then
 316:          SendEmailToGoogleDocs sGoogleEmail, sPDFFile
 317:       end if
 318:  End Function
 319:   
 320:  ------------------------------------------------------------------------------------------
 321:   Returns an array with the file names that match Path.
 322:   The Path string may contain the wildcard characters "*"
 323:   and "?" in the file name component. The same rules apply
 324:   as with the MSDOS DIR command.
 325:   If Path is a directory, the contents of this directory is listed.
 326:   If Path is empty, the current directory is listed.
 327:   Author: Christian dHeureuse (www.source-code.biz)
 328:   
 329:  Public  Function ListDir (ByVal Path)
 330:     Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
 331:     If Path = "" then Path = "*.*"
 332:     Dim Parent, Filter
 333:     if fso.FolderExists(Path) then       Path is a directory
 334:        Parent = Path
 335:        Filter = "*"
 336:       Else
 337:        Parent = fso.GetParentFolderName(Path)
 338:        If Parent = "" Then If Right(Path,1) = ":" Then Parent = Path: Else Parent = "."
 339:        Filter = fso.GetFileName(Path)
 340:        If Filter = "" Then Filter = "*"
 341:        End If
 342:     ReDim a(10)
 343:     Dim n: n = 0
 344:     Dim Folder: Set Folder = fso.GetFolder(Parent)
 345:     Dim Files: Set Files = Folder.Files
 346:     Dim File
 347:     For Each File In Files
 348:   
 349:          bDateFiltered = false
 350:        if bDateFilter = true then
 351:             if sDateFilterType = "c" then
 352:               dDate = File.DateCreated 
 353:             end if            
 354:             if sDateFilterType = "m" then
 355:               dDate = File.DateLastModified
 356:             end if
 357:                       if datediff("d",dDate,sDateFrom) >= 0  or _
                               datediff("d",dDate,sDateTo) < 0 then
 358:                         bDateFiltered = true
 359:                           if bVerbose = true then
 360:                               output.WriteLine File.Name & " skipped (Date Filter), (dc: " & _
File.DateCreated & ", dm: " & File.DateLastModified & ")"
 361:                           end if
 362:                       end if      
 363:              end if
 364:        if  bDateFiltered = false then
 365:            If CompareFileName(File.Name,Filter) Then
 366:                If n > UBound(a) Then ReDim Preserve a(n*2)
 367:                a(n) = File.Path
 368:                n = n + 1
 369:                    else
 370:                           if bVerbose = true then
 371:                               output.WriteLine File.Name & _
                                    " skipped (File Path Filter Argument)"
 372:                           end if                    
 373:            End If
 374:              end if
 375:        Next
 376:     ReDim Preserve a(n-1)
 377:     ListDir = a
 378:     End Function
 379:  -----------------------------------------------------------------------------------------
 380:  Private Function CompareFileName (ByVal Name, ByVal Filter)  (recursive)
 381:     CompareFileName = False
 382:     Dim np, fp: np = 1: fp = 1
 383:     Do
 384:        If fp > Len(Filter) Then CompareFileName = np > len(name): Exit Function
 385:        If Mid(Filter,fp) = ".*" Then     special case: ".*" at end of filter
 386:           If np > Len(Name) Then CompareFileName = True: Exit Function
 387:           End If
 388:        If Mid(Filter,fp) = "." Then      special case: "." at end of filter
 389:           CompareFileName = np > Len(Name): Exit Function
 390:           End If
 391:        Dim fc: fc = Mid(Filter,fp,1): fp = fp + 1
 392:        Select Case fc
 393:           Case "*"
 394:              CompareFileName = CompareFileName2(name,np,filter,fp)
 395:              Exit Function
 396:           Case "?"
 397:              If np <= Len(Name) And Mid(Name,np,1) <> "." Then np = np + 1
 398:           Case Else
 399:              If np > Len(Name) Then Exit Function
 400:              Dim nc: nc = Mid(Name,np,1): np = np + 1
 401:              If Strcomp(fc,nc,vbTextCompare)<>0 Then Exit Function
 402:           End Select
 403:        Loop
 404:     End Function
 405:  -------------------------------------------------------------------------------------------
 406:  Private Function CompareFileName2 (ByVal Name, ByVal np0, ByVal Filter, ByVal fp0)
 407:     Dim fp: fp = fp0
 408:     Dim fc2
 409:     Do                                   skip over "*" and "?" characters in filter
 410:        If fp > Len(Filter) Then CompareFileName2 = True: Exit Function
 411:        fc2 = Mid(Filter,fp,1): fp = fp + 1
 412:        If fc2 <> "*" And fc2 <> "?" Then Exit Do
 413:        Loop
 414:     If fc2 = "." Then
 415:        If Mid(Filter,fp) = "*" Then      special case: ".*" at end of filter
 416:           CompareFileName2 = True: Exit Function
 417:           End If
 418:        If fp > Len(Filter) Then          special case: "." at end of filter
 419:           CompareFileName2 = InStr(np0,Name,".") = 0: Exit Function
 420:           End If
 421:        End If
 422:     Dim np
 423:     For np = np0 To Len(Name)
 424:        Dim nc: nc = Mid(Name,np,1)
 425:        If StrComp(fc2,nc,vbTextCompare)=0 Then
 426:           If CompareFileName(Mid(Name,np+1),Mid(Filter,fp)) Then
 427:              CompareFileName2 = True: Exit Function
 428:              End If
 429:           End If
 430:        Next
 431:     CompareFileName2 = False
 432:     End Function
 433:  -------------------------------------------------------------------------------------------
 434:       
 435:  Sub SendEmailToGoogleDocs(sToEmail, sFileAttachment)
 436:   
 437:  Dim ToAddress
 438:  Dim MessageSubject
 439:  Dim MessageBody
 440:  Dim MessageAttachment
 441:  Dim newMail
 442:   
 443:   
 444:   
 445:  Set oCheckFile = oFso.GetFile(sFileAttachment)
 446:  if oCheckFile.Size > iGoogleDocsSizeLimit then
 447:  File is too large
 448:    output.WriteLine sFileAttachment & " could not be sent to Google Docs. " & _
"It is too large (limit 500KB). File Size: "
& oCheckFile.Size/1024 & "KB"
 449:  else
 450:   
 451:      ToAddress = sToEmail
 452:      MessageSubject = oFso.GetFileName(sFileAttachment)
 453:      MessageBody = ""
 454:   
 455:          
 456:          
 457:          Select Case EmailSendMethod 
 458:          
 459:          Case 1
 460:                Send EMail using MAPI
 461:                
 462:            Set ol = WScript.CreateObject("Outlook.Application")
 463:            Set ns = ol.getNamespace("MAPI")
 464:            ns.logon "","",true,false
 465:             
 466:            Set newMail = ol.CreateItem(olMailItem)
 467:            newMail.Subject = MessageSubject
 468:            newMail.Body = MessageBody &vbCrLf
 469:            
 470:            MessageAttachment = sFileAttachment
 471:            newMail.Attachments.Add(MessageAttachment).Displayname = _
                                             oFso.GetFileName(sFileAttachment)
 472:            
 473:             validate the recipient, just in case…
 474:            Set myRecipient = ns.CreateRecipient(ToAddress)
 475:            myRecipient.Resolve
 476:            
 477:            If Not myRecipient.Resolved Then
 478:                output.WriteLine  sToEmail & _
" is an Unknown recipient, cannot send email with " & sFileAttachment
 479:            Else
 480:               newMail.Recipients.Add(myRecipient)
 481:               newMail.Send
 482:                     if bVerbose = true then
 483:                            output.WriteLine "Email for Google Docs for " & _
                                 sFileAttachment & " was created in our Outlook Outbox folder."
 484:                   end if                     
 485:            End If
 486:        
 487:            Set ol = Nothing
 488:          Case 2
 489:          Send EMail Using CDO
 490:            msgType  = 0 
 491:          
 492:              Set newMail = Wscript.CreateObject("CDO.Message")
 493:      newMail.To = sToEmail
 494:              newMail.From = sToEmail
 495:              newMail.Subject = MessageSubject
 496:              if msgType = TEXTMSG then
 497:            newMail.TextBody = MessageBody &vbCrLf
 498:              else
 499:             newMail.HTMLBody = MessageBody &vbCrLf
 500:              end if
 501:              newMail.AddAttachment  sFileAttachment
 502:             if bVerbose = true then
 503:                output.WriteLine "Email for Google Docs for " & sFileAttachment & _
                     " was sent via CDO"
 504:             end if                     
 505:              
 506:             
 507:        newMail.Configuration.Fields.Item _
 508:              ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
 509:        newMail.Configuration.Fields.Item _
 510:              ("http://schemas.microsoft.com/cdo/configuration/smtpserver") =
 511:        newMail.Configuration.Fields.Item _
 512:              ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
 513:        newMail.Configuration.Fields.Update
 514:          
 515:   
 516:          newMail.Send
 517:   
 518:          set newMail = Nothing
 519:   
 520:      End Select
 521:          
 522:  end if
 523:   
 524:  End Sub     
 525:   
 526:  ------------------------------------------------------------------------------------------
 527:  Sub ForceCscript(bForceRelance) 
 528:  Force to reload the program in non interactive mode 
 529: If (right(Ucase(WScript.FullName),11)="WSCRIPT.EXE") and bForceRelance Then 
 530:   Dim WshShell,args,objArgs,argname, I , sFullCall
 531:   Set WshShell = CreateObject("WScript.Shell") 
 532:   args="" 
 533:   If Wscript.Arguments.Count > 0 Then 
 534:    Set objArgs = Wscript.arguments.Unnamed 
 535:    For I = 0 to objArgs.Count - 1
 536:       if instr(objArgs(I)," ") > 0 then
 537:         args = args & " """ & objArgs(I) & """"
 538:       else 
 539:        args = args & " " & objArgs(I)
 540:       end if         
 541:    Next 
 542:    For each argname in Wscript.arguments.Named 
 543:       if instr(Wscript.arguments.Named.Item(argname)," ") > 0 then
 544:          args = args & " /" & argname & ":""" & Wscript.arguments.Named.Item(argname) & """"
 545:       else
 546:          if Wscript.arguments.Named.Item(argname) = "" then
 547:                  args = args & " /" & argname 
 548:           else
 549:                args = args & " /" & argname & ":" & Wscript.arguments.Named.Item(argname)
 550:           end if        
 551:       end if
 552:    Next
 553:   End If
 554:   sFullCall =  WshShell.ExpandEnvironmentStrings("%COMSPEC%") & " /C cscript.exe """ & _
Wscript.ScriptFullName & """" & args
 555:   
 556:   WshShell.Run  sFullCall,1,False 
 557:   Set WshShell = Nothing 
 558:   WScript.Quit 
 559:  End If 
 560:  End Sub 
 561:   
 562:  =======================================================================
 563:   *** MAIN **************************************
 564:  =======================================================================
 565:   
 566:   
 567:   
 568:   
 569:  set output = wscript.stdout
 570:  bVerbose = arguments.Named.Exists("verbose") Or arguments.Named.Exists("v")
 571:   
 572:  Call EchoUsage()
 573:  Call CheckArgs()
 574:   
 575:  Set ofso = CreateObject("Scripting.FileSystemObject")
 576:  Set owdo = CreateObject("Word.Application")
 577:  Set owdocs = owdo.Documents
 578:   
 579:  sFolder= ""
 580:   
 581:  if not arguments.Named.Exists("dir") then
 582:     Single Document Conversion
 583:     Call DOC2PDF( arguments.Unnamed.Item(0), arguments.Named.Item("o") )
 584:  else
 585:     Batch Conversion
 586:     sFolder = arguments.Named.Item("dir")
 587:     if right(sFolder,1) = "\" then
 588:      sFolder = left(sFolder, Len(sFolder)-1)
 589:     end if 
 590:   
 591:     if Not oFso.FolderExists(sFolder)  then
 592:        output.WriteLine  "Error: Input Folder for Batch Processing does not exist!"
 593:     else
 594:        WScript.Echo sFolder & "\" & arguments.Unnamed.Item(0)
 595:         aFiles = ListDir(sFolder & "\" & arguments.Unnamed.Item(0))
 596:         If UBound(aFiles) = -1 then
 597:            output.WriteLine  "No files found."
 598:         end if
 599:         For Each FileName In aFiles 
 600:            Call DOC2PDF(FileName,"")  
 601:         Next
 602:     end if
 603:   
 604:  end if
 605:   
 606:  owdo.Quit WdDoNotSaveChanges
 607:  Set owdo = Nothing
 608:  Set arguments = Nothing
 609:  Set oFso = Nothing
 610:          

Tuesday, February 10, 2009

FILE_ID.DIZ Stories... A Trip Down Memory Lane

I remember the days when there was no such thing as "file_id.diz" and users had to enter a description for every single file that you uploaded manually. A pain in the ass and often causing poor descriptions of your file directory, because most users (especially in the "Warez Scene" or "Software Pirates Scene") did not have the time to enter very long and descriptive details to every file.

Remember, there was no copy-and-paste nor multi-tasking at that time either.

I know that the AMIGA guys had tools that worked similar to the Windows Clipboard. For that reason was the Amiga scene also the first that introduced the mini ASCII logos of release groups that AMIGA couriers used for the BBS file descriptions to make the files more prominent.

Oldskool (AMIGA style) file_id.diz ASCII design for the Elite Warez PC release group Razor 1911
.
.
Quake III (c)Id–Software final CD–Rip
__________ .
/________ \ –============– : –[01/99]–
: | _\ )__/|______ ____|\ __
_/––– | /___/ _____ / __ \ \/ \ ––\_
\––– | . \ < |/ /____\ | .\_/ ––/
– – | |\______|___________/ | ––– –
– | / .:.nineteeneleven.:.\ | roy
–== |/ ===================== \| ====–

.

Logos were adapted by the PC scene after file_id.diz was introduced by Clark Development Corporation (I believe that was with PC Board V14.5, but I am not 100% sure, it could also been V15.0).

The first ASCII (file_id) logos for PC releases were Amiga style designs, often even the same logos used by the Amiga section of the group, if it had any.

I don't want to show off, but I truly believe that I was the first one who created and used a Block ASCII (PC) style file_id for releases. It was the file_id.diz design for my first PC group that I co-founded called Cardinals in early 1993 and merged entirely into TRSI/Faith in 1994.

Quick Info: What is File_ID.diz?

File_ID.diz is a file name for a small text-file that is added to a compressed archive file (such as ZIP, the de-facto standard for distributing programs via Bulletin Board Systems). The file_id.diz is added by the creator of the program archive file and contains the name of the program and maybe some additional useful information. The sole purpose of the file was the use on bulletin board systems. After a file was uploaded by the user, the BBS software looks for the existence of a file_id.diz file in the archive and uses its content automatically for the description in the file listings, if it finds one. If no file_id.diz was included in the file, the user had to enter a description for it manually.


Cardinals released awesome trainers for PC games with an amount and quality of options never seen before (or after)... Fuck "Dread"! They were "lamer" hehe.


The Cardinals File_ID.DIZ design in Block ASCII Style from early 1993. The First of it's kind? (proof me wrong!)

Also see this and here.

.

▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▐██ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
██▀█ ██▀█ ██▀▄ ██▀▄ ▀ ██▀▄ ██▀█ ██ ██▀█
██ ▀ ██ █ ██ █ ██ █ ██ ██ █ ██ █ ██ ▄ ██▄▄ .
██ ▄ ██▀█ ██▀▄ ██ █ ██ ██ █ ██▀█ ██ █ ▄▄ █
██▄█ ██ █ ██ █ ██▄▀ ██ ██ █ ██ █ ██▄█ ██▄█
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▓█ ▄▄▄▄▄trained▄▄▄▄▄▄
▀ ▓▒ ▀
█ ▒░ █
▄ ▄
▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀
.
.

Cardinals trainer releases did stick-out on the boards, because the big and bold block ASCII logo was hard to miss among those oldskool ASCII logos and regular text.

Hey, If you don't believe me that the Cardinals file_id was the first one using a Block ASCII logo, fine. If you can show me BBS listings from before that where block ASCII was used, I'd appreciate it (actually not, but for history sake, I'd rather proven wrong than making a false claim).

Anyhow. I used Block ASCII for the Cardinals file_id, because I wanted it to stick out. I also used extensively Block ASCII for the Cardinals NFO ASCII that was also not very typical at that time.

Well, the coders had to learn the ALT + numeric ASCII character code combination for 4 block characters... not too much to ask. ALT-176, ALT-177, ALT-178 and ALT-219 :) It wasn't for long that other groups followed suit and also used Block ASCII for their file_id's and I was thinking about even better ways to make file descriptions stick out more.


Block ASCII Codes 101 :)

░ = ASCII character 176
▒ = ASCII character 177
▓ = ASCII character 178
█ = ASCII character 219

In order to generate those characters, you had to press the "ALT" key and enter the ASCII code
on the numeric keyboard (while holding the ALT-Key pressed) and then release the ALT-Key at which
moment it would render the character that represents the entered ASCII value.

I thought about ANSI and created the designs that you can see here in my deviation for testing. They were never used of course. First of all, the ESC sequences would have been a problem in many cases.

Also the length of File descriptions (per line) ... I believe it was 42 characters for most BBS systems, would have posed an issue. The color formatting of the ANSI codes (or PC Board color codes, if you would have used those) limited very much the characters that would remain for the logo itself.

AMIGA-Sysop's and traders would also had have some problems with those... It was an interesting concept for a moment, but failed for very practical reasons.

ansi_file_id_concepts1993 Long, but nice story eh?

I almost forgot about this, but then I found my ANSI file_id.diz designs and it all came back to me :).

I started a collection of file_id.diz artwork that was used by scene groups for their releases. My collection still has a bunch of holes, but it had a good start and already includes over 240 file_ids. I also made the whole collection available as a single ZIP archive for download which includes the ASCII files in original format. You might want to check it out. Enjoy!



Cheers!

Carsten aka Roy/SAC