diff --git a/agents.md b/agents.md
new file mode 100644
index 0000000..9da8721
--- /dev/null
+++ b/agents.md
@@ -0,0 +1,171 @@
+# DocTo — Agent Guide
+
+## Project Overview
+
+DocTo is a Windows command-line utility written in **Delphi (Object Pascal)** that
+converts Microsoft Office documents (Word `.doc`/`.docx`, Excel `.xls`/`.xlsx`,
+PowerPoint `.ppt`/`.pptx`) to other formats (PDF, CSV, TXT, RTF, etc.) via COM
+Automation. Microsoft Word, Excel, or PowerPoint must be installed on the host machine.
+
+
+- Repository: https://github.com/tobya/DocTo
+- Website: https://tobya.github.io/DocTo/
+## Tech Stack
+
+| Layer | Technology |
+|-------|-----------|
+| Language | Delphi (tested with 10.3; compatible with XE4+) |
+| Office integration | Windows COM / Office Interop (Word, Excel, PowerPoint, Visio) |
+| Build system | Delphi IDE / `.dproj` project file |
+| Tests | Batch scripts (`.bat`) in `/test/` | PHP Pest Tests in `/companion`
+| Docs / companion / Test site | Markdown + PHP (`/pages/`, `/companion/`) |
+
+## Repository Layout
+
+```
+docTo/
+├── src/ # All Delphi source files (.pas, .dpr, .dproj)
+│ ├── docto.dpr # Project file (entry point)
+│ ├── MainUtils.pas # Core TDocumentConverter class and shared utilities
+│ ├── baseConfig.pas # Abstract TParamLoader base class for CLI params
+│ ├── configInput.pas # -F / --inputfile parameter handler
+│ ├── configOutput.pas # -OX / --outputextension parameter handler
+│ ├── WordUtils.pas # Word COM interop helpers
+│ ├── ExcelUtils.pas # Excel COM interop helpers
+│ ├── PowerPointUtils.pas # PowerPoint COM interop helpers
+│ ├── PathUtils.pas # Path/directory utilities
+│ ├── ResourceUtils.pas # String resource helpers
+│ ├── datamodSSL.* # Data module for SSL/webhook support
+│ ├── shared/ # Shared/common units
+│ ├── Exceptions/ # Custom exception types
+│ └── res/ # Resource files
+├── test/ # Manual test scripts and fixture files
+│ ├── testDocTo.bat # Main test runner batch script
+│ ├── InputFiles/ # Sample Word/RTF/CSV/XLS input files
+│ ├── inputfilesxl/ # Excel-specific input fixtures
+│ ├── inputfilespp/ # PowerPoint-specific input fixtures
+│ ├── GeneratedFiles/ # Output directory for test conversions
+│ └── GeneratedTestputFiles/
+├── .github/
+│ ├── workflows/ # GitHub Actions (greetings bot)
+│ └── ISSUE_TEMPLATE/
+├── pages/ # GitHub Pages / documentation content
+├── companion/ # Companion tooling
+├── exe/ # Pre-built binaries
+├── readme.md
+└── changes.md
+```
+
+## Architecture
+
+### Core Pattern: TParamLoader
+
+CLI parameters follow a **registration/dispatch** pattern:
+
+- `TParamLoader` (`baseConfig.pas`) — abstract base class with three responsibilities:
+ - `RegisterParams(List)` — adds the parameter key(s) it handles (e.g. `-F`, `--INPUTFILE`) to a lookup list
+ - `Load(Converter, Param, Value)` — applies the parsed value to the `TDocumentConverter` instance
+ - `ShouldDec` — whether parsing should decrement the argument index after processing
+- Each parameter has a dedicated subclass (e.g. `TParamInput`, `TParamOutputExtension`)
+- `TDocumentConverter` (`MainUtils.pas`) is the central domain object passed through all param loaders
+
+### Converters
+
+Three COM-based converter paths:
+- **Word** (default): use `-WD` flag or omit; format constants from `wdSaveFormat`
+- **Excel**: use `-XL` flag; format constants from `xlFileFormat`
+- **PowerPoint**: use `-PP` flag
+
+### Logging
+
+Log levels are integers: `1` ERRORS, `2` STANDARD (default), `5` CHATTY, `9` DEBUG, `10` VERBOSE.
+Use `Converter.logdebug(msg, LEVEL)` for diagnostic output.
+
+## Building
+
+- **Compiler**: Embarcadero Delphi (tested with 10.3+; XE4 and XE7 also supported)
+- **Platform**: Windows only — relies on COM, Word/Excel/PowerPoint interop
+- Open `src/docto.dproj` in the Delphi IDE and build, or use the Delphi command-line compiler (`dcc32`)
+- Output is a single `docto.exe` binary
+
+No external package manager or build script is present. The project has no Linux/macOS build path.
+
+## Code Structure
+
+- Ensure that If blocks always have a begin end section for all branches even if not strictly neccessary.
+
+## Testing
+
+Tests are `.bat` scripts in `/test/`. They call the compiled `docto.exe` and verify output files are produced. Run them directly from a Windows command prompt with Office installed:
+
+Tests are manual batch scripts in `test/`:
+
+```bat
+# Run the main test suite (requires Word/Excel/PowerPoint installed)
+.\test\testDocTo.bat
+```
+
+- Input fixtures live in `test/InputFiles/`, `test/inputfilesxl/`, `test/inputfilespp/`
+- Outputs are written to `test/GeneratedFiles/` and `test/GeneratedTestputFiles/`
+- There is no automated unit-test framework; correctness is verified by inspecting generated files
+
+There is no automated test runner — tests must be run manually on a machine with Microsoft Office installed.
+
+Additional Tests are written as Pest Tests in companion Laravel PHP site in the `/companion/` dir
+
+## Key Concepts for Agents
+
+- **Application flags**: `-WD` (Word), `-XL` (Excel), `-PP` (PowerPoint), `-VS` (Visio). Word is the default.
+- **Three required parameters**: `-F` (input file/dir), `-O` (output file/dir), `-T` (format type, e.g. `wdFormatPDF`).
+- **Format types**: Passed as named constants (e.g. `wdFormatPDF`, `xlCSV`) or integers matching the Office Interop enums.
+- **COM errors**: Office automation can raise `EOleException`. The `-X` flag controls whether DocTo halts or continues on COM errors.
+- **TLB constants**: `Word_TLB_Constants.pas`, `Excel_TLB_Constants.pas`, and `PowerPoint_TLB_Constants.pas` define the Office format enum values.
+
+## Contribution Guidelines
+
+- Open an issue before large PRs to avoid wasted effort.
+- The main development branch is `DocTo` (note: not `main`).
+- Looking for help with: Delphi/VBA features, PHP/Laravel/Pest tests, and documentation.
+- PRs are welcome.
+
+When adding a new conversion feature, add a corresponding test case to `testDocTo.bat` and provide a sample input file under the appropriate `InputFiles*` directory.
+
+## Error Codes
+
+| Code | Meaning |
+|------|---------|
+| 200 | Invalid file format specified |
+| 201 | Insufficient inputs (need -F, -O, -T at minimum) |
+| 202 | Switch requires a value |
+| 203 | Unknown switch |
+| 204 | Input file does not exist |
+| 205 | Invalid parameter value |
+| 220 | Word/Excel/PowerPoint COM error |
+| 221 | Word/Excel/PowerPoint not installed |
+| 400 | Unknown error |
+
+## Adding a New CLI Parameter
+
+1. Create a new unit in `src/` (e.g. `configMyParam.pas`)
+2. Declare a class that extends `TParamLoader`
+3. Implement `RegisterParams` — add the short and long flag names
+4. Implement `Load` — read `Value` and set the appropriate field on `TDocumentConverter`
+5. Implement `ShouldDec` — return `false` unless the param consumes an extra token
+6. Register the new class in the parameter dispatch table in `MainUtils.pas`
+7. Add documentation for the new flag to `readme.md` under "Command Line Help"
+
+## Key Conventions
+
+- Path handling: always call `ExpandFileName` on user-supplied paths to resolve relative references; use `IncludeTrailingBackslash` for directory paths
+- Input validation: use `Converter.HaltWithError(code, message)` to exit with a defined error code
+- COM errors: wrap COM calls in try/except and honour the `-X` (halt-on-error) flag
+- String constants and user-visible messages should be placed in `ResourceUtils.pas` (resource strings), not inlined
+- The main branch is named `DocTo` (not `main`)
+
+## External References
+
+- Word SaveAs format constants: https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.word.wdsaveformat
+- Excel file format constants: https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.xlfileformat
+- Word compatibility mode values: https://msdn.microsoft.com/en-us/library/office/ff192388.aspx
+- Releases: https://github.com/tobya/DocTo/releases
+- Wiki & examples: https://github.com/tobya/DocTo/wiki
diff --git a/companion/app/Console/Commands/docto/CreateConfigObject.php b/companion/app/Console/Commands/docto/CreateConfigObject.php
new file mode 100644
index 0000000..bbde743
--- /dev/null
+++ b/companion/app/Console/Commands/docto/CreateConfigObject.php
@@ -0,0 +1,40 @@
+argument('name');
+ $paramlist = str( $this->option('paramlist'))->explode(',');
+ $pasfile = Blade::render('docto.pasfile.configObject',['paramlist'=>$paramlist, 'name'=>$name]);
+ $storageDisk = Storage::build([
+ 'driver' => 'local',
+ 'root' => base_path('../src/ParamObjects'),
+ ]);
+ $storageDisk->put('config' . $name . '.pas', $pasfile);
+ $this->info('Create New Parameter Config file. ' . 'config' . $name . '.pas' );
+ }
+}
diff --git a/companion/resources/views/docto/pasfile/configObject.blade.php b/companion/resources/views/docto/pasfile/configObject.blade.php
new file mode 100644
index 0000000..b0a87a0
--- /dev/null
+++ b/companion/resources/views/docto/pasfile/configObject.blade.php
@@ -0,0 +1,41 @@
+unit config{{$name}};
+
+interface
+
+uses classes, MainUtils, System.Contnrs, SysUtils,
+ baseConfig;
+
+type
+TParam{{$name}} = class(TParamLoader)
+public
+
+ procedure Load(Converter : TDocumentConverter; Param, Value : String); override;
+ function ShouldDec : Boolean; override;
+ class procedure RegisterParameters(List : TStrings);
+end;
+
+implementation
+
+{ TParam{{$name}} }
+
+procedure TParam{{$name}}.Load(Converter: TDocumentConverter; Param, Value: String);
+begin
+ //
+end;
+
+class procedure TParam{{$name}}.RegisterParameters(List: TStrings);
+begin
+
+ @foreach($paramlist as $param )
+ List.AddPair('{{$param}}', TParam{{$name}}.ClassName, TObject(TParam{{$name}}));
+ @endforeach
+
+end;
+
+
+function TParam{{$name}}.ShouldDec: Boolean;
+begin
+ Result := false;
+end;
+
+end.
diff --git a/companion/tests/Feature/Input/InputFilterPestTest.php b/companion/tests/Feature/Input/InputFilterPestTest.php
index 3cff123..8dacd59 100644
--- a/companion/tests/Feature/Input/InputFilterPestTest.php
+++ b/companion/tests/Feature/Input/InputFilterPestTest.php
@@ -57,7 +57,7 @@
->build();
$output = \Illuminate\Support\Facades\Process::run($doctocmd);
- // print_r($output->output());
+
$outputDirFiles = collect(\Illuminate\Support\Facades\Storage::allFiles($outputfiledir));
expect($outputDirFiles->count())->toBeGreaterThan(0);
diff --git a/companion/tests/Feature/Input/InputSubDirPestTest.php b/companion/tests/Feature/Input/InputSubDirPestTest.php
new file mode 100644
index 0000000..65165ac
--- /dev/null
+++ b/companion/tests/Feature/Input/InputSubDirPestTest.php
@@ -0,0 +1,80 @@
+count() + $subdirfiles->count();
+
+ $doctocmd = \App\Services\DocToCommandBuilder::docto()
+ ->add('-WD')
+ ->add('-f', $testinputfilesdir_temp )
+ ->add('-o', $testoutputdir_temp )
+ ->add('-t', 'wdFormatPDF')
+ ->add('-L',10)
+ ->build();
+
+ $output = \Illuminate\Support\Facades\Process::run($doctocmd);
+ // print_r($output->output());
+ $outputDirFiles = collect(\Illuminate\Support\Facades\Storage::allFiles($outputfiledir));
+
+
+ expect($outputDirFiles->count())->tobe($allfilestotal);
+
+ // ensure -ox parameter is used.
+ $file1 = $outputDirFiles->first();
+ expect(str($file1)->endsWith('.pdf'))->toBeTrue();
+
+});
+
+it('can get all files in base dir but not subdir', function ($command){
+ $inputfiledir = 'inputfiles'. uniqid();
+ $outputfiledir = 'outputfiles_docz' . uniqid();
+ // setup
+ $testinputfilesdir_temp = Storage::path($inputfiledir);
+ $testoutputdir_temp = Storage::path($outputfiledir);
+
+ Storage::createDirectory($outputfiledir);
+
+ $dirfiles = \App\Services\FileGatherService::GatherFiles('plain', $inputfiledir);
+ $subdirfiles = \App\Services\FileGatherService::GatherFiles('plain', $inputfiledir . '\\subdir');
+
+ $allfilestotal = $dirfiles->count() + $subdirfiles->count();
+
+ $doctocmd = \App\Services\DocToCommandBuilder::docto()
+ ->add('-WD')
+ ->add('-f', $testinputfilesdir_temp )
+ ->add('-o', $testoutputdir_temp )
+ ->add('-t', 'wdFormatText')
+ ->add($command) // should not load files from /subdir
+ ->add('-L',10)
+ ->build();
+
+ $output = \Illuminate\Support\Facades\Process::run($doctocmd);
+ // print_r($output->output());
+ $outputDirFiles = collect(\Illuminate\Support\Facades\Storage::allFiles($outputfiledir));
+
+
+ expect($dirfiles->count())->toBe(5);
+ expect($outputDirFiles->count())->toBe($dirfiles->count());
+ expect($outputDirFiles->count())->toBeLessThan($allfilestotal);
+
+ // ensure -ox parameter is used.
+ $file1 = $outputDirFiles->first();
+ expect(str($file1)->endsWith('.txt'))->toBeTrue();
+
+})->with([
+ ['--NO-RECURSE'],
+ ['--NO-SUBDIR'],
+ ['--NO-SUBDIRS'],
+
+]);
diff --git a/companion/tests/Feature/VersionPestTest.php b/companion/tests/Feature/VersionPestTest.php
index de0e480..5b5f6ab 100644
--- a/companion/tests/Feature/VersionPestTest.php
+++ b/companion/tests/Feature/VersionPestTest.php
@@ -23,7 +23,7 @@
$outputString = $result->output();
// find at begining out output
- expect(str($outputString)->take(100)->toString())->toContain('DocTo Version: 1.16.46');
+ expect(str($outputString)->take(100)->toString())->toContain('DocTo Version: 1.16');
});
diff --git a/src/ExtraFiles.res b/src/ExtraFiles.res
index 29a996c..727ef9b 100644
Binary files a/src/ExtraFiles.res and b/src/ExtraFiles.res differ
diff --git a/src/MainUtils.pas b/src/MainUtils.pas
index 735688f..9c9780d 100644
--- a/src/MainUtils.pas
+++ b/src/MainUtils.pas
@@ -33,7 +33,7 @@ interface
MSVISIO = 4;
- DOCTO_VERSION = '1.16.2'; // dont use 0x - choco needs incrementing versions.
+ DOCTO_VERSION = '1.16.6'; // dont use 0x - choco needs incrementing versions.
DOCTO_VERSION_NOTE = ' x64 Release ';
type
@@ -80,7 +80,6 @@ TDocumentConverter = class
procedure SetIgnore_MACOSX(const Value: boolean);
procedure SetEncoding(const Value: Integer);
procedure SetSkipDocsWithTOC(const Value: Boolean);
- procedure HaltWithConfigError(ErrorNo: Integer; Msg: String);
procedure SetList_ErrorDocs(const Value: Boolean);
procedure SetList_ErrorDocs_Seconds(const Value: Integer);
@@ -99,6 +98,7 @@ TDocumentConverter = class
procedure SetDocStructureTags(const Value: boolean);
procedure SetBitmapMissingFonts(const Value: boolean);
procedure Setsheets(const Value: TStrings);
+ function GetParamHandlers: TStrings;
protected
@@ -155,9 +155,6 @@ TDocumentConverter = class
procedure SetOutputFileFormatString(const Value: String);
procedure SetOutputLog(const Value: Boolean);
procedure SetOutputLogFile(const Value: String);
- function IsValidFormat(FormatID : Integer): Boolean;
-
- procedure HaltWithError(ErrorNo:Integer; Msg : String);
procedure SetLogToFile(const Value: Boolean);
procedure SetLogFilename(const Value: String);
procedure ListFiles(const PathName, FileName: string; const SubDir: boolean; outFiles: TStrings);
@@ -174,14 +171,7 @@ TDocumentConverter = class
procedure SetIsDirOutput(const Value: Boolean);
procedure SetIsFileOutput(const Value: Boolean);
procedure SetLogLevel(const Value: integer);
- property InputIsFile : Boolean read FInputIsFile write SetIsFileInput;
- property InputIsDir : Boolean read FInputIsDir write SetIsDirInput;
- property OutputIsFile : Boolean read FOutputIsFile write SetIsFileOutput;
- property OutputIsDir : Boolean read FOutputIsDir write SetIsDirOutput;
- property OutputIsStdOut : Boolean read FOutputIsStdOut write SetOutputIsStdOut;
- property DoSubDirs : Boolean read FDoSubDirs write SetDoSubDirs;
- property OutputExt : string read FOutputExt write SetOutputExt;
- property LogLevel : integer read FLogLevel write SetLogLevel;
+
property RemoveFileOnConvert: boolean read FRemoveFileOnConvert write SetRemoveFileOnConvert;
property Ignore_MACOSX : boolean read FIgnore_MACOSX write SetIgnore_MACOSX;
property List_ErrorDocs : Boolean read FList_ErrorDocs write SetList_ErrorDocs ;
@@ -203,7 +193,6 @@ TDocumentConverter = class
property WordConstants : TResourceStrings read getWordConstants;
- property OfficeAppName : String read FOfficeAppName write FOfficeAppName;
// Events
@@ -256,14 +245,14 @@ TDocumentConverter = class
procedure LogError(Msg: String);
function ConvertErrorText(Msg: String) : String;
function CallWebHook(Params: String) : string;
- FUNCTION AfterConversion(InputFile, OutputFile: String):string;
- Function OnConversionError(InputFile, OutputFile, Error: String):string;
+ function AfterConversion(InputFile, OutputFile: String):string;
+ function OnConversionError(InputFile, OutputFile, Error: String):string;
Procedure LoadFileList();
procedure LogResourceHelp(HelpResName : String);
procedure LogVersionInfo(ForceReload : boolean = true);
-
+ procedure HaltWithError(ErrorNo:Integer; Msg : String);
procedure LogWordFormats();
procedure LogExcelFormats();
@@ -282,7 +271,12 @@ TDocumentConverter = class
Property LogToFile : Boolean read FLogToFile write SetLogToFile;
property LogFilename: String read FLogFilename write SetLogFilename;
Property Version : String read FVersionString;
+ property LogLevel : integer read FLogLevel write SetLogLevel;
property HaltOnWordError : Boolean read FHaltOnWordError write SetHaltOnWordError;
+ property OfficeAppName : String read FOfficeAppName write FOfficeAppName;
+ function IsValidFormat(FormatID : Integer): Boolean;
+ procedure HaltWithConfigError(ErrorNo: Integer; Msg: String);
+ function LookupFormatByName(const FormatName: String): Integer;
property SkipDocsWithTOC : Boolean read FSkipDocsWithTOC write SetSkipDocsWithTOC;
property SkipDocsExist : Boolean read FSkipDocsExist write FSkipDocsExist;
property InputExtension: String read GetExtension write SetExtension;
@@ -291,6 +285,15 @@ TDocumentConverter = class
property BookMarkSource: Integer read FBookMarkSource;
property pdfExportRange : Integer read FPdfExportRange_Word write SetPDfExportRange ;
+ property InputIsFile : Boolean read FInputIsFile write SetIsFileInput;
+ property InputIsDir : Boolean read FInputIsDir write SetIsDirInput;
+ property OutputIsFile : Boolean read FOutputIsFile write SetIsFileOutput;
+ property OutputIsDir : Boolean read FOutputIsDir write SetIsDirOutput;
+ property OutputIsStdOut : Boolean read FOutputIsStdOut write SetOutputIsStdOut;
+ property DoSubDirs : Boolean read FDoSubDirs write SetDoSubDirs;
+ property OutputExt : string read FOutputExt write SetOutputExt;
+
+ property ParamHandlers : TStrings read GetParamHandlers;
property IsWord : Boolean read getIsWord;
property IsExcel : Boolean read getIsExcel;
@@ -305,6 +308,9 @@ TDocumentConverter = class
implementation
+uses baseConfig, ConfigOutput, ConfigInput, configLogLevel, configFormat,
+ configNoRecurse ,configCompatibility
+;
{ TConsoleLog }
@@ -689,8 +695,14 @@ function TDocumentConverter.Execute: string;
if ConversionInfo.Successful then
begin
- // logInfo('File Converted: ' + ConversionInfo.OutputFile);
- logInfo('Files Converted: ' + sLineBreak + fOutputFiles.Text);
+ logInfo('File Converted: ' + ConversionInfo.OutputFile);
+
+ // when excel converts sheets each is a seperate file and
+ // listed in fOutputFiles
+ if(fOutputFiles.Count > 0) then
+ begin
+ logInfo('Files Converted: ' + fOutputFiles.Text);
+ end;
// Check if file needs to be deleted.
if RemoveFileOnConvert then
@@ -847,6 +859,17 @@ function TDocumentConverter.IsValidFormat(FormatID: Integer): Boolean;
end;
end;
+function TDocumentConverter.LookupFormatByName(const FormatName: String): Integer;
+var
+ idx : Integer;
+begin
+ idx := Formats.IndexOfName(FormatName);
+ if idx > -1 then
+ Result := StrToInt(Formats.Values[FormatName])
+ else
+ Result := -1;
+end;
+
function TDocumentConverter.ChooseConverter(Params: TStrings) : integer;
var f , iParam, idx: integer;
pstr : string;
@@ -935,9 +958,12 @@ procedure TDocumentConverter.LoadConfig(Params: TStrings);
HelpStrings: TResourceStrings;
tmpext : String;
valueBool : Boolean;
- X: Integer;
- Sval : String;
+ X, O: Integer;
+ Sval : string;
+ ParamHandlerClass : TClass;
+ ParamHandler : TParamLoader;
+ paramHandleridx : integer;
begin
// Initialise
iParam := 0;
@@ -961,6 +987,7 @@ procedure TDocumentConverter.LoadConfig(Params: TStrings);
+
While iParam <= Params.Count -1 do
begin
@@ -983,9 +1010,33 @@ procedure TDocumentConverter.LoadConfig(Params: TStrings);
// jump to next id + value
inc(iParam,2);
+ logdebug(Self.ParamHandlers.Values[id],errors);
+ if Self.ParamHandlers.Values[id] <> '' then
+ begin
+
+
+ // retrieve the Handler from list.
+ paramHandleridx := ParamHandlers.IndexOfName(id);
+
+ // Retrieve insance of class from handler list.
+ ParamHandlerClass := TCLASS(ParamHandlers.Objects[paramHandleridx]);
-if (id = '-XL') or
+ // Create instance of class.
+ LogDebug(ParamHandlerClass.ClassName + 'before cast',ERRORS);
+ ParamHandler := TParamLoader(ParamHandlerClass.Create());
+
+ // load parameters
+ LogDebug(ParamHandler.ClassName + ' ' + id + ' :: ' + value,ERRORS);
+ ParamHandler.Load(Self,id,Value);
+
+ if ParamHandler.ShouldDec then
+ begin
+ dec(iParam);
+ end;
+
+ end
+ else if (id = '-XL') or
(id = '--EXCEL') or
(id = '-WD') or
(id = '--WORD') or
@@ -1027,21 +1078,6 @@ procedure TDocumentConverter.LoadConfig(Params: TStrings);
end
- else if (id = '-OX') or
- (id = '--OUTPUTEXTENSION') then
- begin
-
- //If the first character isn't . add it.
- if value[1] = '.' then
- begin
- FOutputExt := value;
- end
- else
- begin
- FOutputExt := '.' + value;
- end;
-
- end
else if (id = '-F') or
(id = '--INPUTFILE') then
begin
@@ -1089,14 +1125,6 @@ procedure TDocumentConverter.LoadConfig(Params: TStrings);
end;
end
- else if (id = '--NO-RECURSE') or
- (id = '--NO-SUBDIR') or
- (id = '--NO-SUBDIRS') then
- begin
- FDoSubDirs := false;
- LogInfo('Loading files from directory but not subdirectories',CHATTY);
- dec(iparam);
- end
else if (id = '--STDOUT') then
BEGIN
OutPutIsStdOut := true;
@@ -1109,15 +1137,7 @@ procedure TDocumentConverter.LoadConfig(Params: TStrings);
begin
InputExtension := value;
end
- else if ( id = '-L')
- OR (id = '--LOGLEVEL') then
- begin
- if isNumber(value) then
- begin
- LogLevel := strtoint(value);
- LogInfo('Log Level Set To:' + IntToStr(LogLevel),LogLevel);
- end
- end
+
else if (id = '-Q') or
(id = '--QUIET') then
begin
@@ -1126,38 +1146,7 @@ procedure TDocumentConverter.LoadConfig(Params: TStrings);
// Doesn't require a value
dec(iParam);
end
- else if (id = '-T') or (id = '-TF') or
- (id = '--FORMAT') or (id = '--FORCEFORMAT') then
- begin
- if IsNumber(value) then
- begin
- FOutputFileFormat := strtoint(value);
- //If not forcing, and the format is invalid by list, then raise error.
- if (not (id = '-TF')) and ( not IsValidFormat(FOutputFileFormat)) then
- begin
- HaltWithConfigError(200, 'File Format ' + value + ' is invalid, please see help. -h. To force use, use -TF');
- end;
- end
- else // string format such as 'XLcsv'
- begin
- FOutputFileFormatString := value;
-
- idx := formats.IndexOfName(FOutputFileFormatString);
- if idx > -1 then
- begin
- OutputFileFormat := strtoint(formats.Values[OutputFileFormatString]);
-
- end
- else if idx = -1 then
- begin
- HaltWithConfigError(200,'File Format ' + OutputFileFormatString + ' is an invalid ' + OfficeAppName + ' file extension , please see help. -h');
-
- end;
- end;
- logdebug('Type Integer is: ' + inttostr(FOutputFileFormat), VERBOSE);
-
- end
else if (id = '-C') or
(id = '--COMPATIBILITY') then
begin
@@ -1731,6 +1720,19 @@ function TDocumentConverter.GetExtension: String;
+function TDocumentConverter.GetParamHandlers: TStrings;
+begin
+ Result := TStringList.Create;
+
+ TParamInput.RegisterParameters(Result);
+ TParamOutputExtension.RegisterParameters(Result);
+ TParamLogLevel.RegisterParameters(Result);
+ TParamFormat.RegisterParameters(Result);
+ TParamNoRecurse.RegisterParameters(Result);
+ TParamCompatibility.RegisterParameters(Result);
+
+end;
+
function TDocumentConverter.getIsExcel: Boolean;
begin
Result := MSExcel = FAppID;
diff --git a/src/ParamObjects/baseConfig.pas b/src/ParamObjects/baseConfig.pas
new file mode 100644
index 0000000..e105800
--- /dev/null
+++ b/src/ParamObjects/baseConfig.pas
@@ -0,0 +1,53 @@
+unit baseConfig;
+
+interface
+
+uses Classes,System.Contnrs,
+
+ MainUtils;
+
+type
+
+
+
+TParamLoader = class abstract
+ private
+ fParamID : string;
+ protected
+ function GetParamID: String; virtual;
+ procedure SetParamID(const Value: String); virtual;
+
+
+public
+
+
+ procedure Load(Converter : TDocumentConverter; Param, Value : String); virtual; abstract;
+ function ShouldDec : Boolean; virtual; abstract;
+
+ class procedure RegisterParameters(List : TStrings);
+
+ Property ParamID : String read GetParamID Write SetParamID;
+
+end;
+
+
+implementation
+
+{ TParamLoader }
+
+function TParamLoader.GetParamID: String;
+begin
+ result := FParamID;
+end;
+
+class procedure TParamLoader.RegisterParameters(List: TStrings);
+begin
+ //
+end;
+
+procedure TParamLoader.SetParamID(const Value: String);
+begin
+ FParamID := Value;
+end;
+
+end.
diff --git a/src/ParamObjects/configCompatibility.pas b/src/ParamObjects/configCompatibility.pas
new file mode 100644
index 0000000..5beb598
--- /dev/null
+++ b/src/ParamObjects/configCompatibility.pas
@@ -0,0 +1,40 @@
+unit configCompatibility;
+
+interface
+
+uses classes, MainUtils, System.Contnrs, SysUtils,
+ baseConfig;
+
+type
+TParamCompatibility = class(TParamLoader)
+public
+
+ procedure Load(Converter : TDocumentConverter; Param, Value : String); override;
+ function ShouldDec : Boolean; override;
+ class procedure RegisterParameters(List : TStrings);
+end;
+
+implementation
+
+{ TParamCompatibility }
+
+procedure TParamCompatibility.Load(Converter: TDocumentConverter; Param, Value: String);
+begin
+ //
+end;
+
+class procedure TParamCompatibility.RegisterParameters(List: TStrings);
+begin
+
+ List.AddPair('-C', TParamCompatibility.ClassName, TObject(TParamCompatibility));
+ List.AddPair('--COMPATIBILITY', TParamCompatibility.ClassName, TObject(TParamCompatibility));
+
+end;
+
+
+function TParamCompatibility.ShouldDec: Boolean;
+begin
+ Result := false;
+end;
+
+end.
diff --git a/src/ParamObjects/configFormat.pas b/src/ParamObjects/configFormat.pas
new file mode 100644
index 0000000..b726210
--- /dev/null
+++ b/src/ParamObjects/configFormat.pas
@@ -0,0 +1,70 @@
+unit configFormat;
+
+interface
+
+uses classes, MainUtils, System.Contnrs, SysUtils,
+ baseConfig;
+
+type
+TParamFormat = class(TParamLoader)
+public
+
+ procedure Load(Converter : TDocumentConverter; Param, Value : String); override;
+ function ShouldDec : Boolean; override;
+ class procedure RegisterParameters(List : TStrings);
+end;
+
+implementation
+
+{ TParamFormat }
+
+procedure TParamFormat.Load(Converter: TDocumentConverter; Param, Value: String);
+var
+ ForceFormat : Boolean;
+ FormatInt : Integer;
+begin
+ ForceFormat := (Param = '-TF') or (Param = '--FORCEFORMAT');
+
+ if IsNumber(Value) then
+ begin
+ FormatInt := StrToInt(Value);
+ Converter.OutputFileFormat := FormatInt;
+ if (not ForceFormat) and (not Converter.IsValidFormat(FormatInt)) then
+ begin
+ Converter.HaltWithConfigError(200, 'File Format ' + Value +
+ ' is invalid, please see help. -h. To force use, use -TF');
+ end;
+ end
+ else // string format such as 'wdFormatPDF', 'xlCSV'
+ begin
+ Converter.OutputFileFormatString := Value;
+ FormatInt := Converter.LookupFormatByName(Value);
+ if FormatInt > -1 then
+ begin
+ Converter.OutputFileFormat := FormatInt;
+ end
+ else
+ begin
+ Converter.HaltWithConfigError(200, 'File Format ' + Value +
+ ' is an invalid ' + Converter.OfficeAppName + ' file extension , please see help. -h');
+ end;
+ end;
+
+ Converter.LogDebug('Type Integer is: ' + IntToStr(Converter.OutputFileFormat), VERBOSE);
+end;
+
+class procedure TParamFormat.RegisterParameters(List: TStrings);
+begin
+ List.AddPair('-T', TParamFormat.ClassName, TObject(TParamFormat));
+ List.AddPair('--FORMAT', TParamFormat.ClassName, TObject(TParamFormat));
+ List.AddPair('-TF', TParamFormat.ClassName, TObject(TParamFormat));
+ List.AddPair('--FORCEFORMAT', TParamFormat.ClassName, TObject(TParamFormat));
+end;
+
+
+function TParamFormat.ShouldDec: Boolean;
+begin
+ Result := false;
+end;
+
+end.
diff --git a/src/ParamObjects/configInput.pas b/src/ParamObjects/configInput.pas
new file mode 100644
index 0000000..55a1d2d
--- /dev/null
+++ b/src/ParamObjects/configInput.pas
@@ -0,0 +1,90 @@
+unit configInput;
+
+interface
+
+uses classes, MainUtils,System.Contnrs, Sysutils,
+ baseConfig;
+
+type
+TParamInput = class(TParamLoader)
+public
+
+
+ procedure Load(Converter : TDocumentConverter; Param, Value : String); override;
+ function ShouldDec : Boolean; override;
+
+ class procedure RegisterParameters(List : TStrings);
+end;
+
+implementation
+
+{ TParamOutput }
+
+procedure TParamInput.Load(Converter: TDocumentConverter; Param,
+ Value: String);
+var tmppath : string;
+begin
+
+ // Before doing anything else expand file name to remove any relative paths.
+ Converter.InputFile := ExpandFileName(Value);
+
+ Converter.logdebug('[TPARAMINput]Input File is: ' + Converter.InputFile,CHATTY);
+
+ tmppath := ExtractFilePath(Converter.InputFile);
+
+ // If we are given a filename with no path, get currentdir and add to file.
+ if (tmppath = '') then
+ begin
+ tmppath := GetCurrentDir();
+ tmppath := IncludeTrailingBackslash(tmppath);
+ Converter.InputFile := tmppath + Converter.InputFile;
+ end;
+
+ if (FileExists(Converter.InputFile) = false) and (DirectoryExists(Converter.InputFile) = false) then
+ begin
+ Converter.HaltWithError(204,'EInput file ' + Converter.InputFile + ' does not exist.');
+ end
+ else if (FileExists(Converter.InputFile)) then
+ begin
+ Converter.InputIsFile := true;
+ Converter.InputIsDir := false;
+ end
+ else if (DirectoryExists(Converter.InputFile)) then
+ begin
+ Converter.InputIsFile := false;
+ Converter.InputIsDir := true;
+
+ // Create Absolute path from any relative path
+ Converter.InputFile := ExpandFileName(Converter.InputFile);
+ end;
+
+
+ // Set Output Directory to Input Directry at this stage. This ensure if no
+ // output directory (-o) is specified, then it will default to same as
+ // input dir. If output has been supplied as param it will overwrite later.
+ if Converter.OutputFile = '' then
+ begin
+ Converter.OutputFile := IncludeTrailingBackslash(tmppath);
+ Converter.OutputIsDir := true;
+ end;
+
+
+
+
+end;
+
+
+class procedure TParamInput.RegisterParameters(List: TStrings);
+begin
+ List.AddPair('-F',TParamInput.Classname,TOBJECT(TParamInput) );
+ List.AddPair('--INPUTFILE',TParamInput.Classname,TOBJECT(TParamInput) );
+end;
+
+
+
+function TParamInput.ShouldDec: Boolean;
+begin
+ Result := false;
+end;
+
+end.
diff --git a/src/ParamObjects/configLogLevel.pas b/src/ParamObjects/configLogLevel.pas
new file mode 100644
index 0000000..32d5767
--- /dev/null
+++ b/src/ParamObjects/configLogLevel.pas
@@ -0,0 +1,44 @@
+unit configLogLevel;
+
+interface
+
+uses classes, MainUtils, System.Contnrs, SysUtils,
+ baseConfig;
+
+type
+TParamLogLevel = class(TParamLoader)
+public
+
+ procedure Load(Converter : TDocumentConverter; Param, Value : String); override;
+ function ShouldDec : Boolean; override;
+ class procedure RegisterParameters(List : TStrings);
+end;
+
+implementation
+
+{ TParamLogLevel }
+
+procedure TParamLogLevel.Load(Converter: TDocumentConverter; Param,
+ Value: String);
+begin
+ if IsNumber(Value) then
+ begin
+ Converter.LogLevel := StrToInt(Value);
+ Converter.LogInfo('Log Level Set To:' + IntToStr(Converter.LogLevel), Converter.LogLevel);
+ Converter.LogInfo('TConfigLogLevel Sets:' + IntToStr(Converter.LogLevel), Converter.LogLevel);
+ end;
+end;
+
+class procedure TParamLogLevel.RegisterParameters(List: TStrings);
+begin
+ List.AddPair('-L', TParamLogLevel.ClassName, TObject(TParamLogLevel));
+ List.AddPair('--LOGLEVEL', TParamLogLevel.ClassName, TObject(TParamLogLevel));
+end;
+
+
+function TParamLogLevel.ShouldDec: Boolean;
+begin
+ Result := false;
+end;
+
+end.
diff --git a/src/ParamObjects/configNoRecurse.pas b/src/ParamObjects/configNoRecurse.pas
new file mode 100644
index 0000000..b7818a4
--- /dev/null
+++ b/src/ParamObjects/configNoRecurse.pas
@@ -0,0 +1,46 @@
+unit configNoRecurse;
+
+interface
+
+uses classes, MainUtils, System.Contnrs, SysUtils,
+ baseConfig;
+
+type
+TParamNoRecurse = class(TParamLoader)
+public
+
+ procedure Load(Converter : TDocumentConverter; Param, Value : String); override;
+ function ShouldDec : Boolean; override;
+ class procedure RegisterParameters(List : TStrings);
+end;
+
+implementation
+
+{ TParamNoRecurse }
+
+procedure TParamNoRecurse.Load(Converter: TDocumentConverter; Param, Value: String);
+begin
+ Converter.DoSubDirs := false;
+
+ Converter.LogInfo('Loading files from directory but not subdirectories',CHATTY);
+
+end;
+
+class procedure TParamNoRecurse.RegisterParameters(List: TStrings);
+begin
+
+ List.AddPair('--NO-RECURSE', TParamNoRecurse.ClassName, TObject(TParamNoRecurse));
+ List.AddPair('--NO-SUBDIR', TParamNoRecurse.ClassName, TObject(TParamNoRecurse));
+
+ List.AddPair('--NO-SUBDIRS', TParamNoRecurse.ClassName, TObject(TParamNoRecurse));
+
+
+end;
+
+
+function TParamNoRecurse.ShouldDec: Boolean;
+begin
+ Result := true;
+end;
+
+end.
diff --git a/src/ParamObjects/configOutput.pas b/src/ParamObjects/configOutput.pas
new file mode 100644
index 0000000..2c7ab97
--- /dev/null
+++ b/src/ParamObjects/configOutput.pas
@@ -0,0 +1,54 @@
+unit configOutput;
+
+interface
+
+uses classes, MainUtils,System.Contnrs,
+ baseConfig;
+
+type
+TParamOutputExtension = class(TParamLoader)
+public
+
+ procedure Load(Converter : TDocumentConverter; Param, Value : String); override;
+ function ShouldDec : Boolean; override;
+
+ class procedure RegisterParameters(List : TStrings);
+
+end;
+
+implementation
+
+{ TParamOutput }
+
+procedure TParamOutputExtension.Load(Converter: TDocumentConverter; Param,
+ Value: String);
+begin
+
+ //If the first character isn't . add it.
+ if value[1] = '.' then
+ begin
+ Converter.OutputExt := value;
+ end
+ else
+ begin
+ Converter.OutputExt := '.' + value;
+ end;
+
+
+end;
+
+class procedure TParamOutputExtension.RegisterParameters(List: TStrings);
+begin
+ List.AddPair('-OX',TParamOutputExtension.Classname, TObject(TParamOutputExtension));
+ List.AddPair('--OUTPUTEXTENSION',TParamOutputExtension.Classname, TObject(TParamOutputExtension));
+
+end;
+
+
+
+function TParamOutputExtension.ShouldDec: Boolean;
+begin
+ Result := false;
+end;
+
+end.
diff --git a/src/docto.dpr b/src/docto.dpr
index 565389c..8e80259 100644
--- a/src/docto.dpr
+++ b/src/docto.dpr
@@ -37,7 +37,14 @@ uses
VisioUtils in 'VisioUtils.pas',
Visio_TLB in 'Visio_TLB.pas',
DynamicFileNameGenerator in 'shared\DynamicFileNameGenerator.pas',
- DocToExceptions in 'Exceptions\DocToExceptions.pas';
+ DocToExceptions in 'Exceptions\DocToExceptions.pas',
+ baseConfig in 'ParamObjects\baseConfig.pas',
+ configInput in 'ParamObjects\configInput.pas',
+ configOutput in 'ParamObjects\configOutput.pas',
+ configLogLevel in 'ParamObjects\configLogLevel.pas',
+ configFormat in 'ParamObjects\configFormat.pas',
+ configNoRecurse in 'ParamObjects\configNoRecurse.pas',
+ configCompatibility in 'ParamObjects\configCompatibility.pas';
var
i, Converter : integer;
diff --git a/src/docto.dproj b/src/docto.dproj
index 61848f1..59d1e46 100644
--- a/src/docto.dproj
+++ b/src/docto.dproj
@@ -8,7 +8,7 @@
None
DCC32
18.8
- Win64
+ Win32
3
Win32
@@ -305,6 +305,10 @@
+
+
+
+
diff --git a/src/docto.dproj.local b/src/docto.dproj.local
index 25748df..fbd059d 100644
--- a/src/docto.dproj.local
+++ b/src/docto.dproj.local
@@ -12,46 +12,51 @@
2025/11/19 14:43:31.000.175,C:\Development\github\docto\src\Unit1.pas=C:\Development\github\docto\src\Exceptions\DocToExceptions.pas
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/docto.res b/src/docto.res
index d95ca05..3923ef8 100644
Binary files a/src/docto.res and b/src/docto.res differ