3

In a Delphi application I am using since years the following code to export xlxs to pdf:

function TExportTool.ExportExcelToPDF(aFileName, aNewFileName: String): Boolean;
// reference : http://embarcadero.newsgroups.archived.at/public.delphi.oleautomation/200811/081103142.html
// unluckily the link above is dead
{- Sheet is counted from 1 and upwards !! }
Var
  App,oWB,oSheet : OleVariant;
begin
  Result := False;
  App:= CreateOleObject('Excel.Application');
  Try
    App.Visible:= 0;
    oWb := App.WorkBooks.Open(ExpandUNCFileName(afilename),1);  // Open read only
    Try
     oSheet := oWB.ActiveSheet;
     oSheet.ExportAsFixedFormat(0,  //xlTypePDF is constant 0
                            aNewFileName,
                            EmptyParam, 
                            EmptyParam, 
                            EmptyParam, // this should be IgnorePrintAreas
                            EmptyParam,
                            EmptyParam,
                            EmptyParam,
                            EmptyParam
                            );
    Finally
    End;
    Result := True;
  Finally
    App.Quit;
    App:= UnAssigned;
  End;
end;

// IMPROVED WORKING CODE FOLLOWS

function TExportTool.ExportExcelToPDF(aFileName, aNewFileName: String): Boolean;
// reference : http://embarcadero.newsgroups.archived.at/public.delphi.oleautomation/200811/081103142.html
{- Sheet is counted from 1 and upwards !! }

procedure RestoreOriginalPrintArea (oSheet: OleVariant);
// Excel loses print area settings in non-English version of application when file is opened using automation:
// https://stackoverflow.com/questions/71379893/exportasfixedformats-ignoreprintareas-parameter-seems-not-to-have-effect
var
  i:Integer;
begin
  for  i:= 1 to oSheet.Names.Count do
  begin
   if VarToStr(oSheet.Names.Item(i).Name).EndsWith('!Print_Area') then
   begin
     oSheet.PageSetup.PrintArea:='Print_area';
     Break;
   end;
  end;
end;

Var
  App,oWB,oSheet : OleVariant;
  i:Integer;
begin
  Result := False;
  App:= CreateOleObject('Excel.Application');
  Try
    App.Visible:= 0;
    oWb := App.WorkBooks.Open(ExpandUNCFileName(afilename),1);  // Open read only
    Try
     oSheet := oWB.ActiveSheet;
     RestoreOriginalPrintArea(oSheet);  // workaround
     oSheet.ExportAsFixedFormat(0,  //xlTypePDF is constant 0
                            aNewFileName,
                            0, // standard quality = 0, Max quality = 1
                            false, //include doc properties
                            false, //ignore print area
                            EmptyParam,
                            EmptyParam,
                            EmptyParam,
                            EmptyParam
                            );
    Finally
    End;
    Result := True;
  Finally
    oWB.Close(false); // better to close the WorkBook too
    App.Quit;
    App:= UnAssigned;
  End;
end;

Now i realized that the pdf created with this code behave like when saving to pdf from Excel using the option "Ignore Print areas" (it is one of the options of the export to pdf from Excel feature).

So I decided to "uncheck" that checkbox also from code and I studied the parameters of ExportAsFixedFormat (reference here).

The fifth parameter is IgnorePrintAreas, so I was assuming that passing False to it, the print areas would have been ignored.

I tried several common sense solution, including:

  • passing only that parameter (passing either True or False )
  • passing all the first 5 parameters (just in case they are mandatory at runtime)

but no result: the pdf created by my application still "ignores the print areas".

Does anyone has a suggestion or has experience on this specific subject to give me a pointer to fix this issue?

Thanks.

UPDATE

Thanks to the useful accepted answer I appended to the code above the solution for reference, notice two things:

  1. the RestoreOriginalPrintArea procedure that contains the workaround
  2. the call to oWB.Close(false) at the end
UnDiUdin
  • 14,924
  • 39
  • 151
  • 249
  • 1
    I've seen it before - it probably isn't an issue with export routine. If you check the value of `oSheet.PageSetup.PrintArea` you will probably get an empty string instead of whatever is set there. If you assign any valid range address in your code - export should work as expected. I don't know any fix for this issue. – BrakNicku Mar 08 '22 at 10:32
  • @BrakNicku thanks for your comment. I tried to set `oSheet.PageSetup.PrintArea`in code just before calling `ExportAsFixedFormat`and in fact what happens is that the pdf is explored using the print area i set, so the print area is not ignored, anyway setting the printarea does not work as you suggested. Somehow my problem is that "IgnorePrintAreas" parameter is "ignored". – UnDiUdin Mar 09 '22 at 10:29
  • First you say, that if you set `PrintArea` in code then only this part is exported, and then _anyway setting the printarea does not work as you suggested_. So I'm not sure what you mean. – BrakNicku Mar 09 '22 at 10:31
  • @BrakNicku my goal is to export to pdf by using the `IgnorePrintAreas` setting. Excel manages to print well if that setting is `false`. Anyway I notice that the pdf generated in code behaves like if that parameter is `true`, but I did not mean to set it true, in fact Iwas not passing any value to it. So I tried to force it to be `false` by passing `false` in the call, but this seems ignored. – UnDiUdin Mar 09 '22 at 10:49
  • But again: it is not a problem of the export routine, but excel when used in automation does not read PrintArea at all, so no matter what you do - print, export, it will always use whole sheet. Do a test: make `App.Visible:=1`, remove `App.Quit` and test your function on a file where the print area is set. Check the file - the print area settings are no longer there. – BrakNicku Mar 09 '22 at 11:10
  • 2
    I think I've found a workaround. Add a line `oSheet.PageSetup.PrintArea:='Print_Area';` in try...except block before export. If it works - I'll post an answer with some explanation. – BrakNicku Mar 09 '22 at 14:39
  • @BrakNicku he suggested workaround works! Thanks a lot. Write an answer and I will accept it, what i did was adding `try oSheet.PageSetup.PrintArea:='Print_Area'; except end;` just before calling `oSheet.ExportAsFixedFormat`. it does not fo in the except part but it works. before I tried to pass a real print area ($A$1:$T$32') but the result was bad. – UnDiUdin Mar 09 '22 at 15:42
  • 1
    OK, but I'll do it tomorrow, it was quite interesting to investigate:) – BrakNicku Mar 09 '22 at 15:44

1 Answers1

3

Root cause of error:

Excel loses print area settings in non-English version of application when file is opened using automation.

Why this is happening:

When you define print area in a sheet, Excel internally creates a named range. It has two properties defining its name:

  1. Name this property is always of the form WorksheetsName!Print_Area (if the sheet's name contains some special characters it is also enclosed in single quotes).
  2. NameLocal has similar structure, but the second part is translated into the language of the application.

This is what it looks like when you open the file in Excel and inspect these properties in VBA, but when you open the same file using automation (for example using the code in question), then NameLocal is no longer translated. This bug causes the named range to not be recognized correctly as print area. oSheet.PageSetup.PrintArea returns an empty string.

Workaround:

Restore original print area after opening the file using:

oSheet.PageSetup.PrintArea:='Print_Area';

This line of code will throw an exception when there was no print area defined in sheet, so there are two options:

  1. Place the line inside try..except block.
  2. Iterate the Names collection and look for a Name ending with !Print_Area, for example:
var i:Integer;
for  i:= 1 to oSheet.Names.Count do
begin
  if  VarToStr(oSheet.Names.Item(i).Name).EndsWith('!Print_Area') then
  begin
    oSheet.PageSetup.PrintArea:='Print_area';
    Break;
  end;
end;

Other important change:

Because the file could have been modified you also need to add:

oWB.Close(false); //do not save changes

before closing the application, otherwise each call to this function would result in another Excel process still running invisible.

BrakNicku
  • 5,935
  • 3
  • 24
  • 38