unit rHTMLTools;
(*This entire unit was created by K. Toppenberg, starting on 5/27/05
  It will hold additional functions to support HTML display of notes
  and printing of HTML notes.                                           *)
interface
  uses Windows, SysUtils, Classes, Printers, ComCtrls,
       ShDocVw, {//kt added ShDocVw 5-2-05 for TWebBrowser access}
       Dialogs,
       Forms,
       Registry, {elh   6/19/09}
       ORFn;    {//kt for RedrawActivate}
  var
    DesiredHTMLFontSize : byte;
    CPRSDir : string;
    URL_CPRSDir : string;  //This is CPRSDir, but all '\'s are converted to '/'s
  CONST
    CPRS_DIR_SIGNAL = '$CPRSDIR$';
    CPRS_CACHE_DIR_SIGNAL = CPRS_DIR_SIGNAL+'\Cache\';
    ALT_IMG_TAG_CONVERT = 'alt="convert to ' + CPRS_DIR_SIGNAL +'"';
  procedure PrintHTMLReport(Lines: TStringList; var ErrMsg: string;
                            PtName, DOB, VisitDate, Location:string; Application : TApplication);  //kt added 5-2-05
  function  IsHTML(Lines : TStrings): boolean; overload;
  function  IsHTML(Line : String): boolean; overload;
  function  HasHTMLTags(Text: string) : boolean;
  procedure FixHTML(Lines : TStrings);
  function  FixHTMLCRLF(Text : String) : string;
  procedure SplitToArray (HTMLText: string; Lines : TStrings);
  procedure StripBeforeAfterHTML(Lines,OutBefore,OutAfter : TStrings);
  function  UnwrapHTML(HTMLText : string) : string; 
  function  WrapHTML(HTMLText : string) : string; 
//  function  WaitForBrowserOK(MaxSecDelay: integer; Application : TApplication) : boolean;
  function  CheckForImageLink(Lines: TStrings; ImageList : TStringList) : boolean;
  function  ProtectHTMLSpaces(Text : String) : string;
  function  Text2HTML(Lines : TStrings) : String; overload;
  function  Text2HTML(text : string) : String;    overload;
  procedure SetRegHTMLFontSize(Size: byte);
  procedure RestoreRegHTMLFontSize;
  procedure SetupHTMLPrinting(Name,DOB,VisitDate,Location,Institution : string);
  procedure RestoreIEPrinting;
  function ExtractDateOfNote(Lines : TStringList) : string;
  Procedure ScanForSubs(Lines : TStrings);
  Procedure InsertSubs(Lines : TStrings);
  function HTTPEncode(const AStr: string): string;
implementation
  uses fNotes,
       fImages,
       Messages,
       Graphics, //For color constants
       fTMGPrintingAnimation,
       ExtCtrls,
       uTemplateFields,
       fTemplateDialog,
       StrUtils; 
  type
    TPrinterEvents = class
    public
      SavedDefaultPrinter : string;
      LastChosenPrinterName : string;
      RestorePrinterTimer : TTimer;       
      PrintingNow : boolean;
      procedure HandleRestorePrinting (Sender: TObject);
      Constructor Create;
      Destructor Destroy; override;
    end;  
   
  var
    PrinterEvents : TPrinterEvents;
    SubsFoundList : TStringList;
  const CRLF = #$0D#$0A;
  
  function GetCurrentPrinterName : string;
   //var ResStr: array[0..255] of Char;
  begin
  //GetProfileString('Windows', 'device', '', ResStr, 255);
  //Result := StrPas(ResStr);
    if (Printer.PrinterIndex > 0)then begin
      Result := Printer.Printers[Printer.PrinterIndex];
    end else begin
      Result := '';
    end;
  end;
  procedure SetDefaultPrinter(PrinterName: String) ;
  var
      j                    : Integer;
      Device, Driver, Port : PChar;
      HdeviceMode          : THandle;
      aPrinter             : TPrinter;
  begin    
     Printer.PrinterIndex := -1;
     getmem(Device, 255) ;
     getmem(Driver, 255) ;
     getmem(Port, 255) ;
     aPrinter := TPrinter.create;
     j := Printer.Printers.IndexOf(PrinterName);
     if j >= 0 then begin
       aprinter.printerindex := j;
       aPrinter.getprinter(device, driver, port, HdeviceMode) ;
       StrCat(Device, ',') ;
       StrCat(Device, Driver ) ;
       StrCat(Device, Port ) ;
       WriteProfileString('windows', 'device', Device) ;
       StrCopy( Device, 'windows' ) ;
       //SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, Longint(@Device)) ;
     end;
     Freemem(Device, 255) ;
     Freemem(Driver, 255) ;
     Freemem(Port, 255) ;
     aPrinter.Free;
  end;    
  procedure Wait(Sec : byte; Application : TApplication);
  var   StartTime : TDateTime; 
  const OneSec = 0.000012;  
  begin
    StartTime := GetTime; 
    repeat
      Application.ProcessMessages;
    until (GetTime-StartTime) > (OneSec*Sec);
  end;
  
  procedure PrintHTMLReport(Lines: TStringList; var ErrMsg: string; 
                            PtName, DOB, VisitDate, Location:string;
                            Application : TApplication);
  //      Web browser printing options:
  //        OLECMDEXECOPT_DODEFAULT       Use the default behavior, whether prompting the user for input or not.
  //        OLECMDEXECOPT_PROMPTUSER      Execute the command after obtaining user input.
  //        OLECMDEXECOPT_DONTPROMPTUSER  Execute the command without prompting the user.
  {Notice:  When IE is asked to print, it immediately returns from the function,
           but the printing has not yet occured.  If UI is requested, then the
          printing will not start until after the user selects a printer and
         presses [OK].  I could not find any reliable way to determine when the
        print job had been created.  I had to know this event because I need to
       restore some IE settings AFTER the printing has finished.  I even tried to
      get the active windows and see if it was a print dialog.  But IE print dlg
     apparently is owned by another thread than CPRS, because GetActiveWindow would
     not bring back a handle to the printer dialog window.  I therefore told IE
     to print WITHOUT asking which printer via UI.  In that case it prints to the
     system wide default printer.  So I have to set the default printer to the 
     user's choice, and then change it back again.  This is bit of a kludge,
     but I couldn't figure out any other way after hours of trial and error.
     NOTE: I tried to query IE to see if it was able to print, thinking that it
     would return NO if in the process of currently printing.  It didn't work, 
     and would return OK immediately.      
     
     ADDENDUM:  I was getting errors and inconsistent behavior with this, so I
       have decided to try to let the user click a button when the printer has
       been selected.                                         }    
  var
    UseUI          : OleVariant;   
    //NewPrinterName : string;    
    //dlgWinPrinter  : TPrintDialog;
  begin
    //if PrinterEvents.RestorePrinterTimer.Enabled = false then begin
    //  PrinterEvents.SavedDefaultPrinter := GetCurrentPrinterName;
    //end;  
    if PrinterEvents.PrintingNow then exit; // prevent double printing (it has happened)
    try
      rHTMLTools.ScanForSubs(Lines);    //Added to correct Printing issue  elh
      frmNotes.SetDisplayToHTMLvsText([vmView,vmHTML],Lines);  //ActivateHtmlViewer(Lines);
      if frmNotes.HtmlViewer.WaitForDocComplete = false then begin
        ErrMsg := 'The web browser timed out trying to set up document.';
        exit;
      end;
      PrinterEvents.PrintingNow := true;
      SetupHTMLPrinting(PtName,DOB,VisitDate,Location,' ');  {elh 6/19/09} //kt
      frmNotes.HtmlViewer.PrintFinished := false;               
      UseUI := true;
      frmNotes.HtmlViewer.PrintDocument(UseUI);   //Returns immediately, not after printing done.
      frmTMGPrinting.ShowModal;    // Let user show when print job has been launched.
      PrinterEvents.RestorePrinterTimer.Enabled := true; //launch a restore event in 30 seconds
      //RestoreIEPrinting;  //elh - This was omitted from below. Not sure why.  11/10/09
    finally
      PrinterEvents.PrintingNow := false;
    end;
  end;
  (*
  function WaitForBrowserOK(MaxSecDelay: integer; Application : TApplication) : boolean;
  //Returns TRUE if can print
  var
    StartTime : TDateTime;
    Status: OLECMDF;
    MaxDelay,ElapsedTime : Double;
    CanPrint : boolean;
  const
    OneMin = 0.0007;  //note: 0.0007 is about 1 minute
  begin
    StartTime := GetTime;
    MaxDelay := OneMin * MaxSecDelay;
    repeat
      Status := frmNotes.HtmlViewer.QueryStatusWB(OLECMDID_PRINT);  //"can you print?" -- get print command status
      CanPrint := (Status and OLECMDF_ENABLED) > 0;
      ElapsedTime := GetTime-StartTime;
      Application.ProcessMessages;
    until (ElapsedTime > MaxDelay) or CanPrint or frmNotes.HtmlViewer.PrintFinished;
    Result := CanPrint;
  end;
  *)
  Procedure ScanForSubs(Lines : TStrings);
  //Purpose: To scan note for constant $CPRS$ and replace with CPRS's actual directory
  var i,p,p2 : integer;
      tempS : String;
  begin
    SubsFoundList.Clear;
    for i := 0 to Lines.Count-1 do begin
      p := Pos(CPRS_DIR_SIGNAL,Lines.Strings[i]);
      if p>0 then begin
        p := p + Length(CPRS_CACHE_DIR_SIGNAL);
        p2 := PosEx('"',Lines.Strings[i],p);
        tempS := MidStr(Lines.Strings[i],p,(p2-p));
        SubsFoundList.Add(tempS);
        if Pos('file:///',Lines.Strings[i]) > 0 then begin
          Lines.Strings[i] := AnsiReplaceStr(Lines.Strings[i],CPRS_DIR_SIGNAL,URL_CPRSDir);
        end else begin
          Lines.Strings[i] := AnsiReplaceStr(Lines.Strings[i],CPRS_DIR_SIGNAL,CPRSDir);
        end;
        //Ensure images are downloaded before passing page to web browser
      end;
    end;
    frmImages.EnsureImagesDownloaded(SubsFoundList);
  end;
  Procedure InsertSubs(Lines : TStrings);
  //Purpose: To scan a edited note images, and replace references to CPRS's
  //         actual local directory with CPRS_DIR_SIGNAL ('$CPRSDIR$')
  var i,p : integer;
     TempS: string;
  begin
    for i := 0 to Lines.Count-1 do begin
      p := pos(ALT_IMG_TAG_CONVERT,Lines.Strings[i]);
      if p = 0 then continue;
      TempS := Lines.Strings[i];
      Lines.Strings[i] := AnsiReplaceStr(Lines.Strings[i],URL_CPRSDir,CPRS_DIR_SIGNAL);
      if Lines.Strings[i] = TempS then begin  //There is a problem. Replacement failed.
        MessageDlg('Problem converting image path to $CPRSDIR$',mtWarning,[mbOK],0);
      end;
      //TempS := MidStr(Lines.Strings[i],1,p-1);
      //TempS := TempS + MidStr(Lines.Strings[i],p+length(ALT_IMG_TAG_CONVERT),length(Lines.Strings[i])+1);
      //Lines.Strings[i] := TempS;
      Lines.Strings[i] := AnsiReplaceStr(Lines.Strings[i],ALT_IMG_TAG_CONVERT,'IMAGE'); //Remove signal
    end;
  end;
  function IsHTML(Line : String): boolean;
  {Purpose: To look at the Text and determine if it is an HTML document.
   Test used: if document contains  or