eDocEngine VCL
Create documents and reports in 18 formats
Compatibility
Delphi C++Builder

Offering eDocEngine VCL Services Over The Web

Learn to host eDocEngine on a Windows service.
By V. Subhash

The move from PCs to tablets seems unstoppable now. People are abandoning their laptops and desktops and prefer to work with cool IOS and Android tablets. Due to the limitations of battery technology, tablets cannot run apps with the same resource-load as traditional PC applications. For this reason, IT departments all over the world are moving their desktop applications to centralized servers (or "the cloud," as they like to call it). These server-side applications are then accessed from tablets via lightweight frontend applications - either dedicated tablet apps or run-anywhere browser-based Web applications.

In this article, I will use the example of a desktop application written using eDocEngine and then show you how to move it to a server. I will also demonstrate how to provide access to the server application using a frontend Web application. (I have chosen ASP.NET but you can choose your existing Web development platform.)

A Sample Desktop Application

Here is an application built with eDocEngine and TRichView. It allows the end-user to select an RTF document and then the application will convert the document to PDF.

// Allow the end-user to select a RTF document
// and display it
procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then begin
    RichView1.Style := RVStyle1;
    RichView1.LoadRTF(OpenDialog1.FileName);
    RichView1.Format;
    Button2.Enabled := True;
  end;
end;

// Export the displayed RTF document to PDF
procedure TForm1.Button2Click(Sender: TObject);
begin
  gtPDFEngine1.FileName := 'converted_doc.pdf';
  gtPDFEngine1.Page.TopMargin := 1;
  gtPDFEngine1.Page.RightMargin := 1;
  gtPDFEngine1.Page.BottomMargin := 1;
  gtPDFEngine1.Page.LeftMargin := 1;
  gtRichViewInterface1.Engine := gtPDFEngine1;

  RVReportHelper1.RichView.Style := RVStyle1;
  RVReportHelper1.RichView.LoadRTF(OpenDialog1.FileName);

  gtRichViewInterface1.RenderDocument(RVReportHelper1);
  RVReportHelper1.Clear;
end;

If you wish to provide a similar service to tablet users or remote workers, then you need to create a frontend application for them to upload their RTF documents. So, here is sample Web application. You can open it from any device that can run a HTML Web browser.

An ASP.NET Frontend

This ASP.NET application accepts the e-mail address and the RTF file from the end-user. The RTF document is saved to a writable "Uploads" folder under App_Data. It also prefixes the end-user's email address and a custom delimiter to the uploaded file name. (This saves me the hassle of storing all the data in a database.)

Delphi Prism (Oxygene) C#
method _Default.Button1_Click(sender: System.Object; e: System.EventArgs);
var
  sExtension: String;
begin
  if (TextBox1.Text.Length > 4) then begin
    if (FileUpload1.HasFile) then begin
      sExtension := Path.GetExtension(FileUpload1.FileName).ToLower;
      if (sExtension = ".rtf") then begin
        FileUpload1.SaveAs(Server.MapPath("~/App_Data/Uploads/" +
                           TextBox1.Text +
                           " ~~@~~ " +
                           FileUpload1.FileName));

        Label1.Text :=
           'Thank you. Your documented will be converted to PDF and mailed to you.';
        end else begin
          Label1.Text := 'Your files does not look like an RTF document.';
        end;
      end else begin
        Label1.Text := 'You did not select a file.';
      end;
    end else begin
      Label1.Text := 'You did not select a file.';
    end;
  end;
end;
protected void Button1_Click(object sender, EventArgs e) {
  String sExtension;

  if (TextBox1.Text.Length > 4) {
    if (FileUpload1.HasFile) {
      sExtension = Path.GetExtension(FileUpload1.FileName).ToLower();
      if (sExtension == ".rtf") {
        FileUpload1.SaveAs(Server.MapPath("~/App_Data/Uploads/" +
                           TextBox1.Text +
                           " ~~@~~ " +
                           FileUpload1.FileName));
        Label1.Text =
           "Thank you. Your documented will be converted to PDF and mailed to you.";
       } else {
         Label1.Text = "Your files does not look like an RTF document.";
       }
     } else {
       Label1.Text = "You did not select a file";
     }
   } else {
     Label1.Text = "You did not enter a valid e-mail address.";
   }
}

That takes care of the frontend. Now, let us port our eDocEngine application to the server.

An eDocEngine-Powered Backend

While it is possible to convert the RTF document to PDF from within the ASP.NET application (so that user can upload RTF and download PDF), I did not choose that route. If the input RTF document is very big, then conversion can take too long. There is no point in keeping the user waiting. Also, a web page request is expected to finish execution in a fraction of a second, if not whole seconds. When that does not happen, IIS (or whichever web server software your Web application is running on) will kill that request. Web server software will not let a single user hog the server or a handful of users overwhelm it.

"Upload RTF, download PDF" can still be accomplished if you can link your webserver with an application server or a farm of application servers that can take over the actual conversion. After the upload, the Web application can serve a page with a Javascript script that asynchronously requests a unique URL to see if the converted document is available for download. Meanwhile, you can show a "Please wait. Converting…" message accompanied with a rotating hourglass animation. If properly set up, you can create the illusion of almost no waiting time.

An alternative is to run a Windows service on the Web server that does the actual conversion. A big advantage of a Windows service is that it does not suffer from the limitations that the Web application sandbox - short lifecycle, limited security permissions, exposure to attacks from outsiders, etc.

A Windows service can watch the upload folder and perform the conversion when it finds a RTF document. After the conversion, the service can also e-mail the PDF document to the end-user. Here is how:

  1. Open your Delphi IDE and create a new "Service Application" project.
  2. Select the unit in your project and switch to Design view.
  3. In Object Inspector, change the Name property. I have used "Lazy_RTF_2_PDF_Converter".
  4. Change the Display Name property. I have used "My Lazy RTF-to-PDF Converter"
  5. Drop the following components on the form.
    1. TTimer
    2. TRVStyle
    3. TgtPDFEngine
    4. TgtRichViewInterface
    5. TRVReportHelper
  6. Switch to Code view and add the following units to your Uses section.
      StrUtils,
      IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdAttachment,
      IdExplicitTLSClientServerBase, IdMessageClient, IdSMTPBase, IdSMTP,
      IdMessage, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL,
      IdSSLOpenSSL, IdMessageParts;
    
    (I am using Indy with eDocEngine for mailing the converted documents.)
  7. Add the following global variables.
    LogFilePath: string;
    LogFile: TextFile;
    UploadFolderPath: string;
    OutputFolderPath: string;
    
  8. Select the service in Object Inspector and add the following handler for the OnStart event.
    procedure TLazy_RTF_2_PDF_Converter.ServiceStart(Sender: TService;
      var Started: Boolean);
    begin
      // Location of the log file
      LogFilePath := 'I:\2012_05_14_EV_SAS\sas.log';
      // RTF upload folder that needs to be watched
      UploadFolderPath := 'I:\2012_03_30_SAS\App_Data\Uploads\';
      // Folder where the PDF file needs to be created
      OutputFolderPath := 'I:\2012_05_14_EV_SAS\';
    
      if not FileExists(LogFilePath) then begin
        AssignFile(LogFile, LogFilePath);
        Rewrite(LogFile);
        CloseFile(LogFile);
      end;
    end;
    
    In this procedure, modify the variables with paths from your computer.
  9. In Object Inspector, select the timer and set its Interval property to 30000.
  10. Keep the timer selected and add the following handler for the OnTimer event. This procedure will ensure that the service regularly checks (that is, every 30 seconds) the upload folder for RTF documents and convert them. The procedure also deletes the RTF file after it has been converted and mailed out.
    procedure TLazy_RTF_2_PDF_Converter.Timer1Timer(Sender: TObject);
    var
      SearchRec1: TSearchRec;
      MessagePart1: TIdMessagePart;
    begin
      UpdateLog('Finding uploads...');
      if FindFirst(UploadFolderPath + '*.*', faArchive, SearchRec1) = 0 then begin
        UpdateLog('File found. Checking for file locks...');
        if IsFileInUse(UploadFolderPath + SearchRec1.Name) = False then begin
          if RightStr(LowerCase(SearchRec1.Name),3) = 'rtf' then begin
            UpdateLog('Found an RTF file' + SearchRec1.Name + '. Converting it...');
            ConvertRTF2PDF(UploadFolderPath, SearchRec1.Name);
          end;
          UpdateLog('Deleting ' + SearchRec1.Name);
          DeleteFile(UploadFolderPath + SearchRec1.Name);
        end;
      end;
    end;
    
    The ConvertRTF2PDF method referred by this procedure does the same job as in the sample desktop application referred earlier. It does some additional work - identifying the e-mail address and the original file name, and then mailing the converted document to the uploader. It also logs operations and errors to a log file.
  11. Add ConvertRTF2PDF and rest of the code.
    procedure TLazy_RTF_2_PDF_Converter.ConvertRTF2PDF(AFilePath, AFileName: string);
    var
      sPathName: string;
    begin
      sPathName := AFilePath + AFileName;
      gtPDFEngine1.FileName := OutputFolderPath + CatchFileName(AFileName);
    
      UpdateLog('About convert to ' + gtPDFEngine1.FileName + '.pdf');
    
      gtPDFEngine1.Preferences.ShowSetupDialog := False;
      gtPDFEngine1.Preferences.OpenAfterCreate := False;
    
      // Enable automatic e-mailing of converted documents
      gtPDFEngine1.Preferences.EmailAfterCreate := True;
    
      with gtPDFEngine1.EMailSettings do begin
        Host := 'smtp.example.com';
        UserID := 'user@example.com';
        Password := 'secret';
        Port := 25;
        AuthenticationRequired := True;
    
        Subject := 'Season''s greeting';
        Body.Clear;
        Body.Add('Hi,' + Chr(10) + Chr(10) + 'PFA.');
    
        FromName := 'It''s Me';
        FromAddress := 'joe@example.com';
    
        RecipientList.Clear;
        RecipientList.Add(CatchEmailAddress(AFileName));
      end;
    
      try
        // Add default page margins
        gtPDFEngine1.Page.TopMargin := 1;
        gtPDFEngine1.Page.RightMargin := 1;
        gtPDFEngine1.Page.BottomMargin := 1;
        gtPDFEngine1.Page.LeftMargin := 1;
    
        // Set output PDF file name for the engine
        gtRichViewInterface1.Engine := gtPDFEngine1;
        RVReportHelper1.RichView.Style := RVStyle1;
    
        UpdateLog('Loading ' + sPathName);
        // Load a RTF document with a report helper component
        RVReportHelper1.RichView.LoadRTF(sPathName);
    
        UpdateLog('Rendering ' + sPathName);
        // Render PDF using the print output
        gtRichViewInterface1.RenderDocument(RVReportHelper1);
    
        UpdateLog('Clearing memory');
        // Free memory
        RVReportHelper1.Clear;
        RVReportHelper1.RichView.Clear;
    
      except on Err: Exception
        do begin
          UpdateLog(Err.Message);
        end;
      end;
    
    end;
    
    
    function TLazy_RTF_2_PDF_Converter.CatchEmailAddress(AFileName: string): string;
    var
      n: Integer;
    begin
      n := Pos(' ~~@~~ ', AFileName);
      Result := LeftStr(AFileName, n)
    end;
    
    
    function TLazy_RTF_2_PDF_Converter.CatchFileName(AFileName: string): string;
    var
      n: Integer;
      sFileName: string;
    begin
      if Length(AFileName) > 4 then begin
        n := Length(AFileName) - Pos('~~@~~', AFileName) - Length('~~@~~');
        sFileName := RightStr(AFileName, n);
        Result := LeftStr(sFileName, Length(sFileName) - 4);
      end;
    end;
    
    
    function TLazy_RTF_2_PDF_Converter.IsFileInUse( Const FileName: String ): Boolean;
    var
      HFileRes: HFILE;
    begin
      Result := False;
      If not FileExists( FileName ) then Exit;
      HFileRes := CreateFile( PChar( FileName ),
      GENERIC_READ or GENERIC_WRITE, 0, nil,
      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
      Result := ( HFileRes = INVALID_HANDLE_VALUE );
      If not Result then
        CloseHandle( HFileRes );
    end;
    
    
    procedure TLazy_RTF_2_PDF_Converter.UpdateLog(AEntry: string);
    begin
      AssignFile(LogFile, LogFilePath);
      Append(LogFile);
      Writeln(LogFile, DateTimeToStr(Now) + ': ' + AEntry);
      CloseFile(LogFile);
    end;
    
  12. In the ConvertRTF2PDF procedure, modify the EMailSettings property of the PDF engine so that it works with your SMTP server.
  13. Build the project.
  14. Open Command Prompt and install the service - use the pathname of the .exe file with the /install switch.
    (If you ever decide to get rid of the service, use the /uninstall switch.)
  15. Launch Services management console by typing services.msc in the Windows Run dialog box.
  16. Select the service and start it.
  17. Open a browser window and access the Web application. Provide your e-mail address and upload a RTF document.
  18. Check your inbox for the PDF output document.

Note: If you are testing this code, then remember to stop and uninstall the service when you are finished with it.

References

  1. Tech Republic; Creating NT services in Delphi; Bob Swart; June 5, 2001
  2. Programmer's Heaven; Re: checking if a file is locked before calling the reset(file) function; Posted by "Koppis"; 10 May 2006

---o0O0o---

Our .NET Developer Tools
XtremeDocumentStudio .NET

Multi-format document-processing component suite for .NET developers.

PDFOne .NET

A .NET PDF component suite to create, edit, view, print, reorganize, encrypt, annotate, and bookmark PDF documents in .NET applications.

Our Delphi/C++Builder developer tools
XtremeDocumentStudio Delphi

Multi-format document-processing component suite for Delphi/C++Builder developers, covering both VCL and FireMonkey platforms.

eDocEngine VCL

A Delphi/C++Builder component suite for creating documents in over 20 formats and also export reports from popular Delphi reporting tools.

PDFtoolkit VCL

A Delphi/C++Builder component suite to edit, enhance, view, print, merge, split, encrypt, annotate, and bookmark PDF documents.

Our Java developer tools
XtremeDocumentStudio (for Java)

Multi-format document-processing component suite for Java developers.

PDFOne (for Java)

A Java PDF component suite to create, edit, view, print, reorganize, encrypt, annotate, bookmark PDF documents in Java applications.

Our Platform-Agnostic Cloud and On-Premises APIs
StarDocs

Cloud-hosted and On-Premises REST-based document-processing and document-viewing APIs

Privacy | Legal | Feedback | Newsletter | Blog | Resellers © 2002-2017 Gnostice Information Technologies Private Limited. All rights reserved.