source: cprs/branches/tmg-cprs/CPRS-Chart/rHTMLTools.pas@ 736

Last change on this file since 736 was 729, checked in by Kevin Toppenberg, 15 years ago

Added functions to Templates, and Images tab

File size: 35.5 KB
RevLine 
[453]1unit rHTMLTools;
[541]2(*This entire unit was created by K. Toppenberg, starting on 5/27/05
[453]3 It will hold additional functions to support HTML display of notes
[541]4 and printing of HTML notes. *)
[453]5
6interface
7
[541]8 uses Windows, SysUtils, Classes, Printers, ComCtrls,
9 ShDocVw, {//kt added ShDocVw 5-2-05 for TWebBrowser access}
10 Dialogs,
11 Forms,
12 Registry, {elh 6/19/09}
13 ORFn; {//kt for RedrawActivate}
[453]14
[541]15 var
16 DesiredHTMLFontSize : byte;
17
[671]18 procedure PrintHTMLReport(Lines: TStringList; var ErrMsg: string;
19 PtName, DOB, VisitDate, Location:string; Application : TApplication); //kt added 5-2-05
[541]20 function IsHTML(Lines : TStrings): boolean; overload;
21 function IsHTML(Line : String): boolean; overload;
22 function HasHTMLTags(Text: string) : boolean;
23 procedure FixHTML(Lines : TStrings);
24 function FixHTMLCRLF(Text : String) : string;
25 procedure SplitToArray (HTMLText: string; Lines : TStrings);
26 procedure StripBeforeAfterHTML(Lines,OutBefore,OutAfter : TStrings);
27 function UnwrapHTML(HTMLText : string) : string;
28 function WrapHTML(HTMLText : string) : string;
29// function WaitForBrowserOK(MaxSecDelay: integer; Application : TApplication) : boolean;
30 function CheckForImageLink(Lines: TStrings; ImageList : TStringList) : boolean;
31 function ProtectHTMLSpaces(Text : String) : string;
32 function Text2HTML(Lines : TStrings) : String; overload;
33 function Text2HTML(text : string) : String; overload;
34 procedure SetRegHTMLFontSize(Size: byte);
35 procedure RestoreRegHTMLFontSize;
[671]36 procedure SetupHTMLPrinting(Name,DOB,VisitDate,Location,Institution : string);
[541]37 procedure RestoreIEPrinting;
[671]38 function ExtractDateOfNote(Lines : TStringList) : string;
[729]39 Procedure ScanForSubs(Lines : TStrings);
[671]40
[453]41implementation
42
[541]43 uses fNotes,
44 fImages,
45 Messages,
46 Graphics, //For color constants
47 fTMGPrintingAnimation,
[671]48 ExtCtrls,
[729]49 uTemplateFields,
[686]50 fTemplateDialog,
[541]51 StrUtils;
[453]52
[671]53 type
54 TPrinterEvents = class
55 public
56 SavedDefaultPrinter : string;
57 LastChosenPrinterName : string;
58 RestorePrinterTimer : TTimer;
59 PrintingNow : boolean;
60 procedure HandleRestorePrinting (Sender: TObject);
61 Constructor Create;
62 Destructor Destroy; override;
63 end;
64
65 var
66 PrinterEvents : TPrinterEvents;
67
[541]68 const CRLF = #$0D#$0A;
[453]69
[541]70
71 function GetCurrentPrinterName : string;
72 //var ResStr: array[0..255] of Char;
73 begin
74 //GetProfileString('Windows', 'device', '', ResStr, 255);
75 //Result := StrPas(ResStr);
76 if (Printer.PrinterIndex > 0)then begin
77 Result := Printer.Printers[Printer.PrinterIndex];
78 end else begin
79 Result := '';
80 end;
81 end;
[453]82
[541]83 procedure SetDefaultPrinter(PrinterName: String) ;
84 var
85 j : Integer;
86 Device, Driver, Port : PChar;
87 HdeviceMode : THandle;
88 aPrinter : TPrinter;
89 begin
90 Printer.PrinterIndex := -1;
91 getmem(Device, 255) ;
92 getmem(Driver, 255) ;
93 getmem(Port, 255) ;
94 aPrinter := TPrinter.create;
95 j := Printer.Printers.IndexOf(PrinterName);
96 if j >= 0 then begin
97 aprinter.printerindex := j;
98 aPrinter.getprinter(device, driver, port, HdeviceMode) ;
99 StrCat(Device, ',') ;
100 StrCat(Device, Driver ) ;
101 StrCat(Device, Port ) ;
102 WriteProfileString('windows', 'device', Device) ;
103 StrCopy( Device, 'windows' ) ;
104 //SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, Longint(@Device)) ;
105 end;
106 Freemem(Device, 255) ;
107 Freemem(Driver, 255) ;
108 Freemem(Port, 255) ;
109 aPrinter.Free;
110 end;
111
112
113 procedure Wait(Sec : byte; Application : TApplication);
114 var StartTime : TDateTime;
115 const OneSec = 0.000012;
116 begin
117 StartTime := GetTime;
118 repeat
119 Application.ProcessMessages;
120 until (GetTime-StartTime) > (OneSec*Sec);
121 end;
[671]122
[541]123
124 procedure PrintHTMLReport(Lines: TStringList; var ErrMsg: string;
[671]125 PtName, DOB, VisitDate, Location:string;
126 Application : TApplication);
127 // Web browser printing options:
128 // OLECMDEXECOPT_DODEFAULT Use the default behavior, whether prompting the user for input or not.
129 // OLECMDEXECOPT_PROMPTUSER Execute the command after obtaining user input.
130 // OLECMDEXECOPT_DONTPROMPTUSER Execute the command without prompting the user.
131
132 {Notice: When IE is asked to print, it immediately returns from the function,
133 but the printing has not yet occured. If UI is requested, then the
134 printing will not start until after the user selects a printer and
135 presses [OK]. I could not find any reliable way to determine when the
136 print job had been created. I had to know this event because I need to
137 restore some IE settings AFTER the printing has finished. I even tried to
138 get the active windows and see if it was a print dialog. But IE print dlg
139 apparently is owned by another thread than CPRS, because GetActiveWindow would
140 not bring back a handle to the printer dialog window. I therefore told IE
141 to print WITHOUT asking which printer via UI. In that case it prints to the
142 system wide default printer. So I have to set the default printer to the
143 user's choice, and then change it back again. This is bit of a kludge,
144 but I couldn't figure out any other way after hours of trial and error.
145 NOTE: I tried to query IE to see if it was able to print, thinking that it
146 would return NO if in the process of currently printing. It didn't work,
147 and would return OK immediately.
148
149 ADDENDUM: I was getting errors and inconsistent behavior with this, so I
150 have decided to try to let the user click a button when the printer has
151 been selected. }
152
153 var
154 UseUI : OleVariant;
155 //NewPrinterName : string;
156 //dlgWinPrinter : TPrintDialog;
157 begin
158 //if PrinterEvents.RestorePrinterTimer.Enabled = false then begin
159 // PrinterEvents.SavedDefaultPrinter := GetCurrentPrinterName;
160 //end;
161 if PrinterEvents.PrintingNow then exit; // prevent double printing (it has happened)
162
163 try
[729]164 rHTMLTools.ScanForSubs(Lines); //Added to correct Printing issue elh
[671]165 frmNotes.SetDisplayToHTMLvsText([vmView,vmHTML],Lines); //ActivateHtmlViewer(Lines);
166 if frmNotes.HtmlViewer.WaitForDocComplete = false then begin
167 ErrMsg := 'The web browser timed out trying to set up document.';
168 exit;
169 end;
170 PrinterEvents.PrintingNow := true;
171 SetupHTMLPrinting(PtName,DOB,VisitDate,Location,' '); {elh 6/19/09} //kt
172 frmNotes.HtmlViewer.PrintFinished := false;
173 UseUI := true;
174 frmNotes.HtmlViewer.PrintDocument(UseUI); //Returns immediately, not after printing done.
175 frmTMGPrinting.ShowModal; // Let user show when print job has been launched.
176 PrinterEvents.RestorePrinterTimer.Enabled := true; //launch a restore event in 30 seconds
177 //RestoreIEPrinting; //elh - This was omitted from below. Not sure why. 11/10/09
178 finally
179 PrinterEvents.PrintingNow := false;
180 end;
181 end;
182
183(*
184 Safe copy of above. Delete later...
185
186 procedure PrintHTMLReport(Lines: TStringList; var ErrMsg: string;
[541]187 PtName, DOB, Location:string;
188 Application : TApplication);
189 // Web browser printing options:
190 // OLECMDEXECOPT_DODEFAULT Use the default behavior, whether prompting the user for input or not.
191 // OLECMDEXECOPT_PROMPTUSER Execute the command after obtaining user input.
192 // OLECMDEXECOPT_DONTPROMPTUSER Execute the command without prompting the user.
193
194 {Notice: When IE is asked to print, it immediately returns from the function,
195 but the printing has not yet occured. If UI is requested, then the
196 printing will not start until after the user selects a printer and
197 presses [OK]. I could not find any reliable way to determine when the
198 print job had been created. I had to know this event because I need to
199 restore some IE settings AFTER the printing has finished. I even tried to
200 get the active windows and see if it was a print dialog. But IE print dlg
201 apparently is owned by another thread than CPRS, because GetActiveWindow would
202 not bring back a handle to the printer dialog window. I therefore told IE
203 to print WITHOUT asking which printer via UI. In that case it prints to the
204 system wide default printer. So I have to set the default printer to the
205 user's choice, and then change it back again. This is bit of a kludge,
206 but I couldn't figure out any other way after hours of trial and error.
207 NOTE: I tried to query IE to see if it was able to print, thinking that it
208 would return NO if in the process of currently printing. It didn't work,
209 and would return OK immediately. }
210
[671]211
212
[541]213 var
[671]214 UseUI : OleVariant;
215 NewPrinterName : string;
216 dlgWinPrinter : TPrintDialog;
[541]217 begin
[671]218 if PrinterEvents.RestorePrinterTimer.Enabled = false then begin
219 PrinterEvents.SavedDefaultPrinter := GetCurrentPrinterName;
220 end;
[541]221 dlgWinPrinter := TPrintDialog.Create(nil);
222 frmTMGPrinting.Show;
[671]223 //FIX: get printer name for the one used last time somehow...
[541]224 if dlgWinPrinter.Execute then begin //only sets a local printer
225 NewPrinterName := GetCurrentPrinterName;
226 SetDefaultPrinter(NewPrinterName); //Set global setting that IE will use.
[671]227 PrinterEvents.LastChosenPrinterName := NewPrinterName;
[541]228 try
229 frmNotes.SetDisplayToHTMLvsText([vmView,vmHTML],Lines); //ActivateHtmlViewer(Lines);
230 if frmNotes.HtmlViewer.WaitForDocComplete = false then begin
231 ErrMsg := 'The web browser timed out trying to set up document.';
232 exit;
233 end;
234 SetupHTMLPrinting(PtName,DOB,Location,' '); {elh 6/19/09} //kt
235 frmNotes.HtmlViewer.PrintFinished := false;
236 UseUI := false; //UseUI := true;
237 frmNotes.HtmlViewer.PrintDocument(UseUI); //Returns immediately, not after printing done.
[671]238 PrinterEvents.RestorePrinterTimer.Enabled := true; //launch a restore event in 30 seconds
239 Wait(4,Application);
[541]240 //WaitForBrowserOK(10, Application); //wait up to 10 seconds //Note: this doesn't do what I want. Status is immediately OK.
[671]241 //RestoreIEPrinting; {elh 6/19/09} //kt
[541]242 finally //any needed final code goes here.
[671]243 //SetDefaultPrinter(DefaultPrinter);
[541]244 //beep;
[453]245 end;
246 end;
[541]247 dlgWinPrinter.Free;
248 frmTMGPrinting.Hide;
249 end;
[671]250*)
[541]251 (*
252 function WaitForBrowserOK(MaxSecDelay: integer; Application : TApplication) : boolean;
253 //Returns TRUE if can print
254 var
255 StartTime : TDateTime;
256 Status: OLECMDF;
257 MaxDelay,ElapsedTime : Double;
258 CanPrint : boolean;
259 const
260 OneMin = 0.0007; //note: 0.0007 is about 1 minute
261 begin
262 StartTime := GetTime;
263 MaxDelay := OneMin * MaxSecDelay;
264 repeat
265 Status := frmNotes.HtmlViewer.QueryStatusWB(OLECMDID_PRINT); //"can you print?" -- get print command status
266 CanPrint := (Status and OLECMDF_ENABLED) > 0;
267 ElapsedTime := GetTime-StartTime;
268 Application.ProcessMessages;
269 until (ElapsedTime > MaxDelay) or CanPrint or frmNotes.HtmlViewer.PrintFinished;
270 Result := CanPrint;
271 end;
272 *)
273
274 Procedure ScanForSubs(Lines : TStrings);
275 //Purpose: To scan note for constant $CPRS$ and replace with CPRS's actual directory
276 var i : integer;
277 CPRSDir : string;
278 begin
279 for i := 0 to Lines.Count-1 do begin
280 if Pos('$CPRSDIR$',Lines.Strings[i])>0 then begin
281 CPRSDir := ExcludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
282 Lines.Strings[i] := AnsiReplaceStr(Lines.Strings[i],'$CPRSDIR$',CPRSDir);
283 //Ensure images are downloaded before passing page to web browser
284 frmImages.timLoadImagesTimer(nil);
[729]285 //NOTE: This often doesn't get the job completed before passing to browser
286 // ?? check for completion?
[541]287 end;
[453]288 end;
289 end;
290
291
[541]292 function IsHTML(Line : String): boolean;
293 {Purpose: To look at the Text and determine if it is an HTML document.
[686]294 Test used: if document contains <!DOCTYPE HTML" or <HTML> or </BODY> or other tags
[541]295 This is not a fool-proof test...
296 NOTE: **This does NOT call ScanForSubs as the other IsHTML(TStrings) function does. }
[686]297
[541]298 begin
299 Result := false; //default of false
300 Line := UpperCase(Line);
301 if (Pos('<!DOCTYPE HTML',Line) > 0)
[686]302 or (Pos('<HTML>',Line) > 0)
303 or (Pos('<BR>',Line) > 0)
[729]304 or (Pos(HTML_BEGIN_TAG,Line) > 0)
[686]305 or (Pos('<P>',Line) > 0)
306 or (Pos('&NBSP',Line) > 0)
[541]307 or (Pos('</BODY>',Line) > 0)then begin
308 Result := true;
309 end;
310 end;
[453]311
[541]312
313 function IsHTML(Lines : TStrings): boolean;
314 //Purpose: To look at the note loaded into Lines and determine if it is
315 // an HTML document. See other IsHTML(String) function for test used.
316 begin
317 Result := false;
318 if Lines = nil then exit;
319 Result := IsHTML(Lines.Text);
320 if Result = true then ScanForSubs(Lines);
[453]321 end;
322
[541]323
324 function HasHTMLTags(Text: string) : boolean;
325 function GetTag(p1,p2 : integer; var Text : string) : string;
326 var i : integer;
327 begin
328 Result := MidStr(Text,p1, p2-p1);
329 if Result[1] = '/' then Result := MidStr(Result,2,999);
330 i := Pos(' ',Result);
331 if i >0 then Result := MidStr(Result,1,i-1);
332 end;
333
334 var p1,p2: integer;
335 Tag : string;
336 begin
337 Result := False; //default to ignore
338 Text := UpperCase(Text);
339 if (Pos('&NBSP;',Text)>0) then Result := true
340 else if (Pos('<P>',Text)>0) then Result := true
341 else if (Pos('<BR>',Text)>0) then Result := true
342 else if (Pos('<HTML>',Text)>0) then Result := true
343 else begin
344 p1 := Pos('<',Text); if p1 = 0 then exit;
345 p2 := Pos('>',Text); if p2 = 0 then exit;
346 Tag := GetTag(p1,p2,Text);
347 if Tag='' then exit;
348 if Pos('/'+Tag+'>',Text)>0 then result := true;
349 end;
350 {
351 if (Pos('<BR>',Text)>0) or
352 (Pos('</P>',Text)>0) or
353 (Pos('<UL>',Text)>0) or
354 (Pos('</UL>',Text)>0) or
355 (Pos('<LI>',Text)>0) or
356 (Pos('</LI>',Text)>0) or
357 (Pos('<OL>',Text)>0) or
358 (Pos('</OL>',Text)>0) or
359 (Pos('&NBSP;',Text)>0) or
360 (Pos('<P>',Text)>0) then begin
361 Result := true;
362 end;
363 }
364 end;
365
366
367 function LineAfterTag(Lines : TStrings; Tag : string) : integer;
368 //returns index of line directly after tag (-1 if not found)
369 //Note: only 1st tag is found (stops looking after that)
370 var p,i : integer;
371 s,s1,s2 : string;
372 begin
373 result := -1;
374 Tag := UpperCase(Tag);
375 for i := 0 to Lines.Count-1 do begin
376 s := UpperCase(Lines.Strings[i]);
377 p := Pos(Tag,s);
378 if p=0 then continue;
379 if (p+length(Tag)-1) < length(s) then begin //extra stuff after tag on line --> split line
380 s1 := MidStr(Lines.Strings[i],1,p+length(Tag)-1);
381 s2 := MidStr(Lines.Strings[i],p+length(Tag),9999);
382 Lines.Strings[i] := s1;
383 Lines.Insert(i+1,s2);
384 end;
385 //Lines.Insert(i+1,'');
386 result := i+1;
387 break;
388 end;
389 end;
[453]390
[541]391 function LineBeforeTag(Lines : TStrings; Tag : string) : integer;
392 //returns index of newly added blank line, directly before tag (-1 if not found)
393 //Note: only 1st tag is found (stops looking after that)
394 var p,i,idx : integer;
395 s,s1,s2 : string;
396 begin
397 result := -1;
398 idx := -1;
399 Tag := UpperCase(Tag);
400 for i := 0 to Lines.Count-1 do begin
401 s := UpperCase(Lines.Strings[i]);
402 p := Pos(Tag,s);
403 if p>0 then begin
404 idx := i;
405 break;
406 end;
407 end;
408 if idx <> -1 then begin
409 p := Pos(Tag,UpperCase(Lines.Strings[idx]));
410 if p>1 then begin //extra stuff after tag on line --> split line
411 s1 := MidStr(Lines.Strings[idx],1,p-1);
412 s2 := MidStr(Lines.Strings[idx],p,9999);
413 Lines.Strings[idx] := s1;
414 Lines.Insert(idx+1,s2);
415 inc(idx);
416 end;
417 //Lines.Insert(idx-1,'');
418 result := idx;
[453]419 end;
420 end;
421
[541]422 procedure SplitLineAfterTag(Lines : TStrings; Tag : string);
423 //Purpose: To ensure that text that occurs after Tag is split and wrapped
424 // to the next line.
425 //Note: It is assumed that tag is in form of <TAGName> or <SomeReallyLongText...
426 // if a closing '>' is not provided in the tag name, then the part provided
427 // is used for matching, and then a search for the closing '>' is made, and
428 // the split will occur after that.
429 //Note: Only the first instance of Tag will be found, stops searching after that.
430 //Note: Tag beginning and ending MUST occur on same line (wrapping of a long tag NOT supported)
431 var i,p1,p2 : integer;
432 s,s1,s2 : string;
433 begin
434 Tag := UpperCase(Tag);
435 for i := 0 to Lines.Count-1 do begin
436 s := UpperCase(Lines.Strings[i]);
437 p1 := Pos(Tag,s);
438 if p1=0 then continue;
439 p2 := PosEx('>',s,p1);
440 if p2=0 then continue; //this is a problem, no closing '>' found... ignore for now.
441 if p2 = length(s) then break;
442 s1 := MidStr(Lines.Strings[i],1,p2);
443 S2 := MidStr(Lines.Strings[i],p2+1,999);
444 Lines.Strings[i] := s1;
445 Lines.Insert(i+1,s2);
446 break;
447 end;
448 end;
[453]449
[541]450 procedure SplitLineBeforeTag(Lines : TStrings; Tag : string);
451 //Purpose: To ensure that text that occurs before Tag is split and Tag
452 // is wrapped to the next line.
453 //Note: It is assumed that tag is in form of <TAGName> or <SomeReallyLongText...
454 //Note: Only the first instance of Tag will be found, stops searching after that.
455 var i,p1 : integer;
456 s1,s2 : string;
457 begin
458 Tag := UpperCase(Tag);
459 for i := 0 to Lines.Count-1 do begin
460 p1 := Pos(Tag,UpperCase(Lines.Strings[i]));
461 if p1=0 then continue;
462 s1 := MidStr(Lines.Strings[i],1,p1-1);
463 S2 := MidStr(Lines.Strings[i],p1,999);
464 Lines.Strings[i] := s1;
465 Lines.Insert(i+1,s2);
466 break;
467 end;
468 end;
469
470 function IndexOfHoldingLine(Lines : TStrings; Tag : string) : integer;
471 var i : integer;
472 s : string;
473 begin
474 result := -1;
475 Tag := UpperCase(Tag);
476 for i := 0 to Lines.Count-1 do begin
477 s := UpperCase(Lines.Strings[i]);
478 if Pos (Tag,s)=0 then continue;
479 result := i;
[453]480 break;
481 end;
[541]482 end;
483
484 procedure EnsureTrailingBR(Lines : TStrings);
485 var p,i : integer;
486 begin
487 for i := 0 to Lines.Count-1 do begin //Ensure each line ends with <BR>
488 p := Pos('<BR>',Lines.Strings[i]);
489 if p+3=length(Lines.Strings[i]) then continue;
490 Lines.Strings[i] := Lines.Strings[i] + '<BR>';
491 end;
[453]492 end;
493
[541]494 procedure MergeLines(Lines,BeforeLines,AfterLines : TStrings);
495 var i : integer;
496 Result : TStringList;
497 begin
498 Result := TStringList.Create;
499 SplitLineAfterTag(Lines,'<!DOCTYPE HTML');
500 SplitLineBeforeTag(Lines,'</BODY>');
501 Result.Add(Lines.Strings[0]); //Should be line with <!DOCTYPE HTML...
502 for i := 0 to BeforeLines.Count-1 do begin //Add Before-Lines text
503 Result.Add(BeforeLines.Strings[i]);
504 end;
505 for i := 1 to Lines.Count-2 do begin //Add back regular lines text
506 Result.Add(Lines.Strings[i]);
507 end;
508 for i := 1 to AfterLines.Count-1 do begin //Add After-Lines text
509 Result.Add(AfterLines.Strings[i]);
510 end;
511 Result.Add(Lines.Strings[Lines.count-1]); //Should be '</BODY></HTML>' line
[453]512
[541]513 Lines.Assign(Result);
[453]514 end;
515
[541]516 procedure StripBeforeAfterHTML(Lines,OutBefore,OutAfter : TStrings);
517 //Purpose: Strip all text that comes before <!DOCTYPE ... line and store in OutBefore;
518 // Strip all text that comes after </HTML> ... line and store in OutAfter;
519 var i : integer;
520 DocTypeLine,EndHTMLLine: integer;
521 begin
522 OutBefore.Clear;
523 OutAfter.Clear;
524 DocTypeLine := IndexOfHoldingLine(Lines,'<!DOCTYPE HTML');
525 if DocTypeLine>1 then begin
526 for i := 0 to DocTypeLine-1 do OutBefore.Add(Lines.Strings[i]);
527 for i := 0 to DocTypeLine-1 do Lines.Delete(0);
528 end;
529 EndHTMLLine := IndexOfHoldingLine(Lines,'</HTML>');
530 if (EndHTMLLine>0) and (EndHTMLLine < (Lines.Count-1)) then begin
531 for i := EndHTMLLine+1 to Lines.Count-1 do OutAfter.Add(Lines.Strings[i]);
532 for i := EndHTMLLine+1 to Lines.Count-1 do Lines.Delete(EndHTMLLine+1);
533 end;
[686]534 end;
[541]535
536 Function FixHTMLCRLF(Text : String) : string;
537 begin
538 Result := AnsiReplaceText(Text,'<NO DATA>',#$1E); //protect sequences we want
539 Result := AnsiReplaceText(Result,'>'+CRLF,'>'#$1F); //protect sequences we want
540 Result := AnsiReplaceText(Result,CRLF,'<BR>'+CRLF); //Add <BR>'s to CrLf's
[686]541 Result := AnsiReplaceText(Result,'>'#$1F,'><BR>'+CRLF); //Restore sequences we wanted
[541]542 Result := AnsiReplaceText(Result,#$1E,'<NO DATA>'); //Restore sequences we wanted
[453]543 end;
[541]544
[453]545
[541]546 procedure FixHTML(Lines : TStrings); //kt 6/20/09
547 //Purpose: to put header info that VistA adds to note into proper formatting.
548 var BeforeLines,AfterLines : TStringList;
549 begin
550 BeforeLines := TStringList.Create;
551 AfterLines := TStringList.Create;
552 StripBeforeAfterHTML(Lines,BeforeLines,AfterLines);
553 EnsureTrailingBR(BeforeLines);
554 if BeforeLines.Count>0 then begin //Wrap Before-Lines with formatting
555 BeforeLines.Insert(0,'<CODE>');
556 //<---Existing text remains in between --->
557 BeforeLines.Add('</CODE>');
558 BeforeLines.Add('<HR><P>'); //horizontal line
559 end;
560 EnsureTrailingBR(AfterLines);
561 if AfterLines.Count > 0 then begin //Wrap After-Lines with formatting
562 AfterLines.Insert(0,'<P><CODE>');
563 //<---Existing text remains in between --->
564 AfterLines.Add('</CODE>');
565 end;
566 MergeLines(Lines,BeforeLines,AfterLines);
567 BeforeLines.Free;
568 AfterLines.Free;
569 end;
570
571 procedure SplitToArray (HTMLText: string; Lines : TStrings);
572 var tempS : string;
573 InEscapeCode, InTagCode : boolean;
574 i, LastGoodBreakI : integer;
575 TagStart,TagEnd : integer;
576 TagText : string;
577 TextLen : integer;
578 MaxLineLen : integer;
579 begin
580 Lines.Clear;
581 MaxLineLen := 80;
582 Repeat
583 InEscapeCode := False;
584 InTagCode := False;
585 LastGoodBreakI := 0;
586 TextLen := length(HTMLText);
587 TagText := '';
588 TagStart := 0; TagEnd := 0;
589 if TextLen > 80 then TextLen := MaxLineLen;
590 for i := 1 to TextLen do begin
591 if (HTMLText[i] = '<') then begin
592 InTagCode := True;
593 TagStart := i;
594 TagText := '';
595 LastGoodBreakI := i-1;
596 end;
597 if (HTMLText[i] = '&') then begin
598 InEscapeCode := True;
599 LastGoodBreakI := i-1;
600 end;
601 if InEscapeCode and (HTMLText[i] = ';') then begin
602 InEscapeCode := False;
603 LastGoodBreakI := i;
604 end;
605 if InTagCode and (HTMLText[i] = '>') then begin
606 InTagCode := False;
607 TagEnd := i;
608 TagText := UpperCase(MidStr(HTMLText,TagStart+1,(TagEnd-TagStart-1)));
609 LastGoodBreakI := i;
610 end;
611 if (HTMLText[i] = ',') and (MaxLineLen > 80) then begin
612 LastGoodBreakI := i;
613 break;
614 end;
615 if (TagText='BR') or (TagText='/P') then begin
616 LastGoodBreakI := TagEnd;
617 break;
618 end else TagText := '';;
619 if (not InTagCode) and (not InEscapeCode)
620 and (HTMLText[i] = ' ') then LastGoodBreakI := i;
621 end;
622 if LastGoodBreakI > 0 then begin
623 tempS := MidStr(HTMLText,1,LastGoodBreakI); //get next 80 chars, or less if at the end.
624 HTMLText := Rightstr(HTMLText, length(HTMLText)- LastGoodBreakI); //characters 81 ... the end
625 Lines.Add(tempS);
626 end else begin
627 if MaxLineLen < 250 then begin
628 MaxLineLen := 250; //emergency mode
629 end else begin //i.e. couldn't find any break within 250 chars. So just chop off.
630 tempS := MidStr(HTMLText,1,80);
631 HTMLText := Rightstr(HTMLText, length(HTMLText)- 80); //characters 81 ... the end
632 Lines.Add(tempS);
633 end;
634 end;
635 until length(HTMLText)=0;
636 end;
637
[453]638
[541]639 function WrapHTML(HTMLText : string) : string; //kt 6/3/09
640 //Purpose: take HTML formatted text and sure it has proper headers and footers etc.
641 // i.e. 'wrap' partial HTML into formal format.
642 begin
643 if Pos('<BODY>',HTMLText)=0 then HTMLText := '<BODY>' + HTMLText;
644 if Pos('</BODY>',HTMLText)=0 then HTMLText := HTMLText + '</BODY>';
645 if Pos('<HTML>',HTMLText)=0 then HTMLText := '<HTML>' + HTMLText;
646 if Pos('</HTML>',HTMLText)=0 then HTMLText := HTMLText + '</HTML>';
647 if Pos('<!DOCTYPE HTML',HTMLText)=0 then begin
648 HTMLText := '<!DOCTYPE HTML PUBLIC "-//WC3//DTD HTML 3.2//EN">'+ #10#13 + HTMLText;
649 end;
650 result := HTMLText;
651 end;
[453]652
[541]653 function UnwrapHTML(HTMLText : string) : string;
654 //Purpose: take HTML formatted text and remove proper headers and footers etc.
655 // i.e. 'unwrap' formal HTML into partial HTML format.
656 begin
657 HTMLText := piece(HTMLText,'<HTML>',2);
658 HTMLText := piece(HTMLText,'</HTML>',1);
659 HTMLText := piece(HTMLText,'<BODY>',2);
660 HTMLText := piece(HTMLText,'</BODY>',1);
661 result := HTMLText;
662 end;
[453]663
[541]664 function CheckForImageLink(Lines: TStrings; ImageList : TStringList) : boolean;
665 {Purpose: To scan memNote memo for a link to an image. If found, return link(s)
666 input: none:
667 output: Will return a string list holding 1 or more links
668 Notes: Here will be the <img .. > format scanned for:
[453]669
[541]670 Here is some opening text...
671 <img src="http://www.geocities.com/kdtop3/OpenVistA.jpg" alt="Image Title 1">
672 And here is some more text
673 <img src="http://www.geocities.com/kdtop3/OpenVistA_small.jpg" alt="Image Title 2">
674 And the saga continues...
675 <img src="http://www.geocities.com/kdtop3/pics/Image100.gif" alt="Image Title 3">
676 As with html, end-of-lines and white space is not preserved or significant
[453]677 }
678
[541]679 function GetBetween (var Text : AnsiString; OpenTag,CloseTag : string;
680 KeepTags : Boolean) : string;
681 {Purpose: Gets text between Open and Close tags. Removes any CR's or LF's
682 Input: Text - the text to work on. It IS changed as code is removed
683 KeepTags - true if want tag return in result
684 false if tag not in result (still is removed from Text)
685 Output: Text is changed.
686 Result=the code between the opening and closing tags
687 Note: Both OpenTag and CloseTag MUST be present for anything to happen.
688 }
[453]689
[541]690 procedure CutInThree(var Text : AnsiString; p1, p2 : Integer; var s1,s2,s3 : AnsiString);
691 {Purpose: Cut input string Text into 3 parts, with cut points given by p1 & p2.
692 p1 points to first character to be in s2
693 p2 points to last character to be in s2 }
694 begin
695 s1 := ''; s2 := ''; s3 := '';
696 if p1 > 1 then s1 := MidStr(Text, 1, p1-1);
697 s2 := MidStr(Text, p1, p2-p1+1);
698 s3 := MidStr(Text, p2+1, Length(Text)-p2);
699 end;
[453]700
[541]701 var
702 p1,p2 : integer;
703 s1,s2,s3 : AnsiString;
[453]704
[541]705 begin
706 Result := ''; //default of no result.
707
708 p1 := Pos(UpperCase(OpenTag), UpperCase(Text));
709 if (p1 > 0) then begin
710 p2 := Pos(UpperCase(CloseTag),UpperCase(Text)) + Length(CloseTag) -1;
711 if ((p2 > 0) and (p2 > p1)) then begin
712 CutInThree (Text, p1,p2, s1,Result,s3);
713 Text := s1+s3;
714 //Now, remove any CR's or LF's
715 repeat
716 p1 := Pos (Chr(13),Result);
717 if p1= 0 then p1 := Pos (Chr(10),Result);
718 if (p1 > 0) then begin
719 CutInThree (Result, p1,p1, s1,s2,s3);
720 Result := s1+s3;
721 // Text := MidStr(Text,1,p1-1) + MidStr(Text,p1+1,Length(Text)-p1);
722 end;
723 until (p1=0);
724 //Now cut off boundry tags if requested.
725 if not KeepTags then begin
726 p1 := Length(OpenTag) + 1;
727 p2 := Length (Result) - Length (CloseTag);
728 CutInThree (Result, p1,p2, s1,s2,s3);
729 Result := s2;
[453]730 end;
731 end;
732 end;
733 end;
734
[541]735 var
736 Text : AnsiString;
737 LineStr : string;
[453]738
[541]739 begin
740 Result := false; //set default
741 if (ImageList = nil) or (Lines = nil) then exit;
[453]742 ImageList.Clear; //set default
743 Text := Lines.Text; //Get entire note into one long string
744 repeat
[541]745 LineStr := GetBetween (Text, '<img', '>', true);
746 if LineStr <> '' then begin
747 ImageList.Add(LineStr);
[453]748 Result := true;
749 end;
[541]750 until LineStr = '';
[453]751 //Note: The following works, but need to replace removed links
752 // with "[Title]" Work on later...
753 //memNote.Lines.Text := Text;
754 end;
755
[541]756 function ProtectHTMLSpaces(Text : String) : string;
757 begin
758 Result := AnsiReplaceStr(Text, #9, '&nbsp;&nbsp;&nbsp;&nbsp; ');
759 while Pos(' ',Result)>0 do begin //preserve whitespace
760 Result := AnsiReplaceStr(Result, ' ', '&nbsp;&nbsp;');
761 end;
762 end;
[453]763
[541]764 function Text2HTML(Lines : TStrings) : String;
765 //Purpose: Take plain text, and prep for viewing in HTML viewer.
766 //i.e. convert TABs into &nbsp's and add <br> at end of line etc.
767 var i : integer;
768 tempS : string;
769 begin
770 for i := 0 to Lines.Count-1 do begin
771 tempS := TrimRight(Lines.Strings[i]);
772 if i = Lines.Count-1 then tempS := tempS + '<P>'
773 else tempS := tempS + '<BR>';
774 Lines.Strings[i] := tempS;
775 end;
776 Result := ProtectHTMLSpaces(Lines.Text)
777 end;
[453]778
[541]779 function Text2HTML(text : string) : String; overload;
780 var Lines : TStringList;
781 begin
782 Lines := TStringList.create;
783 Lines.Text := text;
784 Result := Text2HTML(Lines);
785 Lines.Free;
786 end;
787
788 type
789 TFontSizeData = record
790 case byte of 1: (Data : array[0..3] of byte);
791 2: (Size : byte; Filler : array[1..3] of byte);
792 end;
793
794 var
795 StoredFontSize : TFontSizeData;
796 FontSizeReg: TRegistry;
797
798 procedure SetRegHTMLFontSize(Size: byte);
799 //Purpose: To set the internet explorer Font Size value via the registry.
800 //Expected input: HTML_SMALLEST, HTML_SMALL, HTML_MEDIUM,HTML_LARGE, HTML_LARGEST
801 // Value should be 0 (smallest) - 4 (largest)
802 const HTML_BLANK : TFontSizeData =(Data: (0,0,0,0));
803 var Value : TFontSizeData;
804
805 begin
806 if Size > 4 then Size := 4;
807 Value := HTML_BLANK; Value.Size := Size;
808 FontSizeReg := TRegistry.Create; //To be freed in RestoreRegHTMLFontSize
809 try
810 FontSizeReg.Rootkey := HKEY_CURRENT_USER;
811 if FontSizeReg.OpenKey('\Software\Microsoft\Internet Explorer\International\Scripts\3', False) then begin
812 FontSizeReg.ReadBinaryData('IEFontSize',StoredFontSize,Sizeof(StoredFontSize));
813 FontSizeReg.WriteBinaryData('IEFontSize',Value,SizeOf(Value));
814 end;
815 finally
816 end;
817 end;
818
819 procedure RestoreRegHTMLFontSize;
820 //Purpose: To restore the Internet Explorer zoom value to a stored value..
821 //elh 6/19/09
822 begin
823 if not assigned(FontSizeReg) then FontSizeReg := TRegistry.Create;
824 try
825 FontSizeReg.WriteBinaryData('IEFontSize',StoredFontSize,SizeOf(StoredFontSize));
826 finally
827 FontSizeReg.Free;
828 end;
829 end;
830
831 var
832 StoredIEHeader, StoredIEFooters : string;
833 Reg : TRegistry;
834
[671]835 procedure SetupHTMLPrinting(Name,DOB,VisitDate,Location,Institution : string);
[541]836 //Purpose: To open the IE header and footer registry keys, save the
837 //current value and then replace with passed patient data. elh 6/19/09
838 //NOTE: There does not seem to be any other way to do this programatically.
839 var NewHeader,NewFooter : string;
840 begin
841 if DesiredHTMLFontSize > 0 then begin
842 SetRegHTMLFontSize(DesiredHTMLFontSize-1); //Downsize by 1 step
843 end;
844 NewHeader := Location + ' &b ' + Institution + ' &b Printed: &d &t';
[671]845 NewFooter := Name + ' (' + DOB + ') &b Note: ' + VisitDate + ' &b &p of &P';
[541]846 Reg := TRegistry.Create; //to be freed in RestoreIEPrinting
847 try
848 Reg.Rootkey := HKEY_CURRENT_USER;
849 if Reg.OpenKey('\Software\Microsoft\Internet Explorer\PageSetup', False) then begin
850 StoredIEFooters := Reg.ReadString('footer');
851 StoredIEHeader := Reg.ReadString('header');
852 Reg.WriteString('header',NewHeader);
853 Reg.WriteString('footer',NewFooter);
854 end;
855 finally
856 end;
857 end;
858
859 procedure RestoreIEPrinting;
860 //Purpose: To restore the IE header and footer registry with the initial value
[671]861 //NOTE: The below function was used to restore the previous value to the registry
862 // but got commented above, so the registry retained the patient's data
863 // to resolve this, we are now setting this to a default value.
[541]864 //elh 6/19/09
865 begin
866 if not assigned(Reg) then Reg := TRegistry.Create;
867 try
[671]868 StoredIEFooters := '&u&b&d'; //Comment this line to restore previous value
869 StoredIEHeader := '&d&b&t&bPage &p of &P'; //Comment this line to restore previous value
[541]870 Reg.WriteString('footer',StoredIEFooters);
871 Reg.WriteString('header',StoredIEHeader);
872 RestoreRegHTMLFontSize;
873 finally
874 Reg.Free;
875 end;
876 end;
877
[671]878 function ExtractDateOfNote(Lines : TStringList) : string;
879 //Scan note and return date found after 'DATE OF NOTE:', if present.
880 var i,p : integer;
881 s : string;
882 begin
883 Result := '';
884 if Lines = nil then exit;
885 for i := 0 to Lines.Count-1 do begin
886 p := Pos('DATE OF NOTE:',Lines.Strings[i]);
887 if p<1 then continue;
888 s := Piece(Lines.Strings[i],'DATE OF NOTE:',2);
889 s := Piece(s,'@',1);
890 Result := Trim(s);
891 end;
892 end;
893
894 //===============================================================
895
896 Constructor TPrinterEvents.Create;
897 begin
898 RestorePrinterTimer := TTimer.Create(frmNotes);
899 RestorePrinterTimer.Enabled := false;
900 RestorePrinterTimer.Interval := 30000; //30 seconds to complete print job.
901 RestorePrinterTimer.OnTimer := HandleRestorePrinting;
902 PrintingNow := false;
903 end;
904
905 Destructor TPrinterEvents.Destroy;
906 begin
907 RestorePrinterTimer.Free;
908 inherited Destroy;
909 end;
910
911
912 procedure TPrinterEvents.HandleRestorePrinting (Sender: TObject);
913 begin
914 if PrinterEvents.PrintingNow then begin
915 RestorePrinterTimer.Enabled := true; // reset timer for later.
916 exit;
917 end;
918 RestorePrinterTimer.Enabled := false;
919 RestoreIEPrinting; {elh 6/19/09} //kt
920 //kt SetDefaultPrinter(SavedDefaultPrinter);
921 //beep;
922 end;
923
924 //===============================================================
925
926
927initialization
[541]928 DesiredHTMLFontSize := 2; //probably overwritten in fNotes initialization
[671]929 PrinterEvents := TPrinterEvents.Create;
930
931finalization
932 PrinterEvents.Free;
[453]933end.
Note: See TracBrowser for help on using the repository browser.