program md2html; {$mode objfpc}{$H+} uses {$IFDEF UNIX} cthreads, {$ENDIF} Classes, SysUtils, CustApp, Markdown.Processors, MarkDown.Elements, Markdown.HtmlRender, MarkDown.Parser; type { TMD2HTMLApplication } TMD2HTMLApplication = class(TCustomApplication) const ShortOptions = 'hfi:o:H:t:'; LongOptions : Array of String = ('help','full','input:','output:','head:','title:'); private FHead : TStrings; FOutput : String; FInputs : Array of string; FHTMLTitle : String; FFull : Boolean; procedure ConvertMarkDown(const aInput, aOutput: string); function CreateOutputFileName(const aInput: string; isMulti: boolean): string; protected procedure DoRun; override; function ProcessOptions : boolean; virtual; procedure Usage(const ErrMsg: string); virtual; public constructor Create(aOwner: TComponent); override; destructor Destroy; override; end; { TMD2HTMLApplication } function TMD2HTMLApplication.CreateOutputFileName(const aInput : string; isMulti : boolean) : string; var lDir : string; begin if isMulti then begin lDir:=Foutput; if lDir<>'' then lDir:=IncludeTrailingPathDelimiter(lDir); Result:=lDir+ChangeFileExt(ExtractFileName(aInput),'.html'); end else if FOutput<>'' then Result:=FOutput else Result:=ChangeFileExt(aInput,'.html'); Writeln(aInput,' -> ',Result); end; procedure TMD2HTMLApplication.ConvertMarkDown(const aInput,aOutput : string); var lRenderer : TMarkDownHTMLRenderer; lParser : TMarkDownParser; lDoc : TMarkDownDocument; lMarkDown,lHTML : TStrings; begin lParser:=Nil; lDoc:=Nil; lMarkDown:=TStringList.Create; try lMarkDown.LoadFromFile(aInput); lParser:=TMarkDownParser.Create(Self); lDoc:=lParser.Parse(lMarkDown); lRenderer:=TMarkDownHTMLRenderer.Create(Self); if FFull then begin lRenderer.Options:=[hoEnvelope,hoHead]; lRenderer.Title:=FHTMLTitle; if assigned(FHead) then lRenderer.Head:=FHead; end; lHTML:=TStringList.Create; lRenderer.RenderDocument(lDoc,lHTML); lHTML.SaveToFile(aOutput); finally lHTML.Free; lRenderer.Free; lDoc.Free; lParser.Free; lMarkDown.Free; end; end; procedure TMD2HTMLApplication.DoRun; var ErrorMsg: String; lInPut,lOutput : String; begin Terminate; ErrorMsg:=CheckOptions(ShortOptions,LongOptions); if (ErrorMsg<>'') or HasOption('h','help') then begin Usage(ErrorMsg); Exit; end; if not ProcessOptions then Exit; For lInput in FInputs do begin lOutput:=CreateOutputFileName(lInput,Length(FInputs)>1); ConvertMarkDown(lInput,lOutput); end; end; function TMD2HTMLApplication.ProcessOptions: boolean; var lFileName : string; begin Result:=False; FInputs:=GetOptionValues('i','input'); if Length(FInputs)=0 then FInputs:=GetNonOptions(ShortOptions,LongOptions); FOutput:=GetOptionValue('o','output'); if Length(FInputs)>1 then If not DirectoryExists(FOutput) then begin Usage('Directory does not exist or is not a directory: '+Foutput); exit; end; FFull:=HasOption('f','full'); FHTMLTitle:=GetOptionValue('t','title'); lFileName:=GetOptionValue('H','head'); if (lFilename<>'') then begin if not FileExists(lFileName) then begin Usage('Head matter file does not exist: '+lFileName); exit; end; FHead:=TStringList.Create; FHead.LoadFromFile(lFileName); end; Result:=True; end; constructor TMD2HTMLApplication.Create(aOwner: TComponent); begin inherited Create(aOwner); StopOnException:=True; end; destructor TMD2HTMLApplication.Destroy; begin FreeAndNil(FHead); inherited Destroy; end; procedure TMD2HTMLApplication.Usage(const ErrMsg : string); begin if ErrMsg<>'' then Writeln('Error: ',ErrMsg); Writeln('Usage: ', ExeName, ' [options]'); Writeln('Where options is one or more of:'); Writeln('-h --help this message'); Writeln('-i --input=FILE Input markdown file'); Writeln('-o --output=FILE Output HTML file'); Writeln('-f --full Output complete HTML file (html/body/head tags))'); Writeln('-H --head=FILE head tag content file'); ExitCode:=Ord(ErrMsg<>''); end; var Application: TMD2HTMLApplication; begin Application:=TMD2HTMLApplication.Create(nil); Application.Title:='Markdown to HTML converter'; Application.Run; Application.Free; end.