hlavicka.png, 18 kB

Calc - Makra

SheetToPDF aneb vývoj jednoho makra

5.10.2006

Byl jsem e-mailem požádan o pomoc při řešení následujícího problému. Bylo třeba uložit do formátu .pdf jeden list Calcu, případně pouze výběr. Zároveň to mělo proběhnout bez zobrazení voleb pro budoucí pdf soubor. K čemu to může být dobré? Tak si například představme, že máme sešit a v něm několik listů s daty nějakých měření, údaji z poboček nebo nějaké měsíční výsledky. Na každém z těchto listů je spousta mezi-výpočtu a poznámek, které ovšem slouží pouze nám, a nechceme je nikde prezentovat. Proto máme na konci ještě jeden list, kde již budou konečné výsledky ze všech listů, v úhledném balení. Pouze tento list je určen k prezentaci. Já takovýto list ve svých pokusech nazývám 'sestava', a pravě pouze tento list se bude ukládat do pdf. Je jasné že mnou navrhovaná makra budeme ukládat do daného sešitu a spouštět nějakým tlačítkem, z panelu nástrojů nebo podobně, vždy když dojde k významné změně na listech mezi-výpočtů a tudíž budeme chtít vytvořit nový pdf dokument.

Poměrně záhy jsem vyloučil návrhy záznamníku typu:

dispatcher.executeDispatch(document,".uno:ExportToPDF","",0,args())
možná by to pomocí toho šlo, ale já si myslím, že tento příkaz vždy vyvolá okno voleb pro nastavení pdf. Šel jsem na to oklikou pomocí metody dokumentu: ' storeToURL ' která uloží dokument jako jiný dokument, přičemž otevřený dokument zůstane původním dokumentem.

sub SheetToPDF_0(sSheetName as string)
dim i as integer
dim arg(0) as new com.sun.star.beans.PropertyValue
const sPATH = "C:\testPDF.pdf"

' skryta vychozi objektova promenna
  with ThisComponent.Sheets

  ' Skryj nezadouci listy
    for i = 0 to .Count-1
      with .getByIndex(i)
        .IsVisible = iif(.Name = sSheetName,true,false)
      end with
    next i

  ' Uloz pdf
    arg(0).Name = "FilterName"
    arg(0).Value = "calc_pdf_Export"
    ThisComponent.storeToURL(ConvertToUrl(sPATH), arg())

  ' Zobraz vsechny listy
    for i = 0 to .Count-1
      .getByIndex(i).IsVisible = true
    next i

  end with
end sub
proceduru voláme:
    SheetToPDF_0("sestava")
uloží do pdf list 'sestava'

Na tomto místě musím upozornit na to, že toto makro nebude správně pracovat pokud je nastavena nějaká tisková oblast. K tomu se ale později také dostaneme. Kód je myslím tak jednoduchý, že nepotřebuje komentář. Jedině snad, že si cestu budoucího souboru pdf, každý jistě upraví sám. Já ji mám nastavenou pro jednoduchost v konstantě sPATH, což ale není vůbec vhodné. Vhodnější je nastavit cestu dle souboru, z kterého tvoříme pdf.

Zanedlouho jsem zjistil další drobnou chybku na kráse. Makro totiž na konci zobrazí všechny listy, i ty které jsme měli původně skryté. Tak jsem makro trochu poupravil:

sub SheetToPDF_1(sSheetName as string)
dim i as integer
dim arg(0) as new com.sun.star.beans.PropertyValue
const sPATH = "C:\testPDF.pdf"

' skryta vychozi objektova promenna
  with ThisComponent.Sheets
    dim bVisible(.Count-1)
  ' Skryj nezadouci listy
    for i = 0 to .Count-1
      with .getByIndex(i)
        bVisible(i) = .IsVisible ' uchovej viditelnost listu
        .IsVisible = iif(.Name = sSheetName,true,false)
      end with
    next i

  ' Uloz pdf
    arg(0).Name = "FilterName"
    arg(0).Value = "calc_pdf_Export"
    ThisComponent.storeToURL(ConvertToUrl(sPATH), arg())

  ' Zobraz puvodni listy
    for i = 0 to .Count-1
      .getByIndex(i).IsVisible = bVisible(i)
    next i

  end with
end sub
proceduru voláme:
    SheetToPDF_1("sestava")
uloží do pdf list 'sestava'

Nyní je stav viditelnosti listů uschován a na konec obnoven.
Pokud budete tisknout pouze list sestava a zároveň budete tento list chtít ukládat do pdf ve stejné podobě jako tisk, tak je toto makro pro vás již hotové. Stačí nastavit tiskové parametry pro list 'sestava' a exportní filtr "calc_pdf_Export" si již tyto parametry přebere. Nesmí být ovšem nastaveny tiskové oblasti na jiných listech, jinak tyto mají při exportu do pdf přednost. Exportní filtr pdf v Calcu totiž pracuje takto:

  1. pokud existují nějaké tiskové oblasti, uloží se do pdf pouze tyto oblasti, bez ohledu na viditelnost listů.
  2. pokud neexistuje ani na jediném listu tisková oblast, uloží se do pdf pouze viditelné listy, a to v pořadí jak jsou v sešitě seřazeny.
Pokud tedy máte nastaven formát stránky pro tisk, skryjeme všechny listy, kromě 'sestava' můžeme dokument uložit pomocí metody 'storeToURL', obnovit viditelnost listů a máme vymalováno.

Pokud se ale stane, že z nějakých důvodů, musíme tisknout i jiné listy, nebo jejich části, a máme nastaveny tiskové oblasti, které se ale v pdf dokumentu objevit nemají, musíme zapřemýšlet dále. Takže v další ukázce jsem uchoval i tiskové oblasti, zrušil jsem je, uložil dokument jako pdf a nakonec jsem obnovil i tiskové oblasti.

sub SheetToPDF_2(sSheetName as string)
dim i as integer
dim arg(0) as new com.sun.star.beans.PropertyValue
dim PA() as object
const sPATH = "C:\testPDF.pdf"

' skryta vychozi objektova promenna
  with ThisComponent.Sheets
  ' promena pro ulozeni viditelnosti listu
    dim bVisible(.Count-1)
   
    for i = 0 to .Count-1
      with .getByIndex(i)
      ' uloz viditelnost listu
        bVisible(i) = .IsVisible
      ' Skryj nezadouci listy
        .IsVisible = iif(.Name = sSheetName,true,false)
      ' uloz oblasti tisku   
        redim preserve PA(i) 
        PA(i) = .PrintAreas  
      ' zrus oblasti tisku   
        .PrintAreas = array()
      end with
    next i

  ' Uloz pdf
    arg(0).Name = "FilterName"
    arg(0).Value = "calc_pdf_Export"
    ThisComponent.storeToURL(ConvertToUrl(sPATH), arg())

    for i = 0 to .Count-1
      with .getByIndex(i)
      ' Zobraz puvodni listy
        .IsVisible = bVisible(i)
      ' Nastav puvodni tiskove oblasti
        .PrintAreas = PA(i)           
      end with
    next i

  end with
end sub
proceduru opět voláme:
    SheetToPDF_2("sestava")
uloží do pdf list 'sestava'

Vypadá to slušně. Nicméně se může stát i případ, že do souboru pdf chceme ukládat jinou oblast, než tiskneme. Tak jsem přidal volitelnou možnost(parametr sRange) pro nastavení oblasti na listu 'sestava' pro uložení do pdf, se zachováním tiskové oblasti pro tisk:

sub SheetToPDF_3(sSheetName as string, optional sRange as string)
dim i as integer
dim arg(0) as new com.sun.star.beans.PropertyValue
dim PA() as object
dim CRA(0) as new com.sun.star.table.CellRangeAddress
const sPATH = "C:\testPDF.pdf"

' Pokud je to nutne nastav vychozi parametry
  if IsMissing(sRange) then sRange = ""

' skryta vychozi objektova promenna
  with ThisComponent.Sheets

    dim bVisible(.Count-1)
 
    for i = 0 to .Count-1
      with .getByIndex(i)
      ' uloz viditelnost listu
        bVisible(i) = .IsVisible
      ' uloz oblasti tisku
        redim preserve PA(i)
        PA(i) = .PrintAreas
      ' zrus oblasti tisku
        .PrintAreas = array()
        if .Name = sSheetName Then                           
        ' Zadany list bude viditelny                         
          .IsVisible = true                                  
        ' Dle potreby se nastavi oblast tisku                
          if sRange <> "" then                               
            CRA(0) = .getCellRangeByName(sRange).RangeAddress
            .setPrintAreas(CRA())                            
          end if                                             
        else
        ' Skryj nezadouci listy
          .IsVisible = false
        end if
 
      end with
    next i

  ' Uloz pdf
    arg(0).Name = "FilterName"
    arg(0).Value = "calc_pdf_Export"
    ThisComponent.storeToURL(ConvertToUrl(sPATH), arg())

  
    for i = 0 to .Count-1
      with .getByIndex(i)
      ' Zobraz puvodni listy
        .IsVisible = bVisible(i)
      ' Nastav puvodni tiskove oblasti
        .PrintAreas = PA(i)
      end with
    next i

  end with
end sub
proceduru voláme:
    SheetToPDF_3("sestava")
uloží do pdf celý list 'sestava'
nebo:
    SheetToPDF_3("sestava", "B2:H100")
uloží do pdf oblast 'B2:H100' z listu 'sestava'

Vzhledem k tomu, že exportní filtr pdf v Calcu přebírá nastavení pro tiskárnu, tak jsem si ještě zaexperimentoval s orientaci stránky. Přidal jsem volitelný parametr sOrientation, pomocí nějž určujeme zda se stránky do dokumentu pdf budou ukládat nastojato(portrait) či naležato(landscape):

sub SheetToPDF_4(sSheetName as string, _
               optional sRange as string, _
               optional sOrientation as string)
dim i as integer
dim arg(0) as new com.sun.star.beans.PropertyValue
dim PA() as object
dim CRA(0) as new com.sun.star.table.CellRangeAddress
const sPATH = "C:\testPDF.pdf"

' Pokud je to nutne nastav vychozi parametry
  if IsMissing(sRange) then sRange = ""
  if IsMissing(sOrientation) then sOrientation = ""

' Nastav orientaci stranky               
  arg(0).Name = "PaperOrientation"       
  with com.sun.star.view.PaperOrientation
    select case lcase(sOrientation)      
      case "portrait"                    
        arg(0).Value = .PORTRAIT         
        ThisComponent.Printer =arg()     
      case "landscape"                   
        arg(0).Value = .LANDSCAPE        
        ThisComponent.Printer =arg()     
    end select                           
  end with                               

' skryta vychozi objektova promenna
  with ThisComponent.Sheets

    dim bVisible(.Count-1)

  ' Priprava listu pro vystup do pdf
    for i = 0 to .Count-1
      with .getByIndex(i)
      ' uloz viditelnost listu
        bVisible(i) = .IsVisible
      ' uloz oblasti tisku
        redim preserve PA(i)
        PA(i) = .PrintAreas
      ' zrus oblasti tisku
        .PrintAreas = array()
        if .Name = sSheetName Then
        ' Zadany list bude viditelny
          .IsVisible = true
        ' Dle potreby se nastavi oblast tisku
          if sRange <> "" then
            CRA(0) = .getCellRangeByName(sRange).RangeAddress
            .setPrintAreas(CRA())
          end if
        else
        ' Skryj nezadouci listy
          .IsVisible = false
        end if
      end with
    next i

  ' Uloz vybrany list do pdf
    arg(0).Name = "FilterName"
    arg(0).Value = "calc_pdf_Export"
    ThisComponent.storeToURL(ConvertToUrl(sPATH), arg())

  ' Vraceni vsech listu do puvodniho stavu
    for i = 0 to .Count-1
      with .getByIndex(i)
      ' Zobraz puvodni listy
        .IsVisible = bVisible(i)
      ' Nastav puvodni tiskove oblasti
        .PrintAreas = PA(i)
      end with
    next i

  end with
end sub
volání:
SheetToPDF_4("sestava")
uloží celý list sestava, s orientaci stránky, jaká je nastavena pro list
SheetToPDF_4("sestava", "B2:H100")
uloží oblast 'B2:H100' z listu 'sestava', s orientaci stránky, jaká je nastavena pro list
SheetToPDF_4("sestava", "B2:H100", "landscape")
uloží oblast 'B2:H100' z listu 'sestava', s orientaci stránky naležato
SheetToPDF_4("sestava", "", "portrait")
uloží celý list sestava, s orientaci stránky nastojato

Jako sRange lze zadat "" pak se oblast nenastavuje a ukládá se celý list. Jako sOrientation máme pouze dvě možnosti 'portrait', nebo 'landscape'. Jiný text v sOrientation neovlivní nastavení orientace stránky, ale použije se výchozí nastavení.

Pokud bychom ale použili v sRange nesmysl místo adresy oblasti, makro by zhavarovalo, proto jsem ve finální verzi použil obsluhu chyby, která zajistí v takovém případě uložení celého listu.
Při experimentování s chybami jsem přišel na to, že lze i parametr sSheetName mít volitelný. Pokud tedy neuvedeme tento parametr, nebo bude nesmyslný, uloží se poslední list sešitu:

sub SheetToPDF(optional sSheetName as string, _
               optional sRange as string, _
               optional sOrientation as string)
dim i as integer
dim arg(0) as new com.sun.star.beans.PropertyValue
dim PA() as object
dim CRA(0) as new com.sun.star.table.CellRangeAddress
const sPATH = "C:\testPDF.pdf"

' skryta vychozi objektova promenna
  with ThisComponent.Sheets

  ' Pokud je to nutne nastav vychozi parametry
    if IsMissing(sSheetName) or sSheetName = "" then _
      sSheetName = .getByIndex(.Count-1).Name         
    if IsMissing(sRange) then sRange = ""
    if IsMissing(sOrientation) then sOrientation = ""

  ' Nastav orientaci stranky
    arg(0).Name = "PaperOrientation"
    with com.sun.star.view.PaperOrientation
      select case lcase(sOrientation)
        case "portrait"
          arg(0).Value = .PORTRAIT
          ThisComponent.Printer =arg()
        case "landscape"
          arg(0).Value = .LANDSCAPE
          ThisComponent.Printer =arg()
      end select
    end with

    dim bVisible(.Count-1)

  ' Priprava listu pro vystup do pdf
    for i = 0 to .Count-1
      with .getByIndex(i)
      ' uloz viditelnost listu
        bVisible(i) = .IsVisible
      ' uloz oblasti tisku
        redim preserve PA(i)
        PA(i) = .PrintAreas
      ' zrus oblasti tisku
        .PrintAreas = array()
        if .Name = sSheetName Then
        ' Zadany list bude viditelny
          .IsVisible = true
        ' Dle potreby se nastavi oblast tisku,
        ' pro neexistujici adresu ale preskoc
          on error goto ERR_NEXT
          if sRange <> "" then
            CRA(0) = .getCellRangeByName(sRange).RangeAddress
            .setPrintAreas(CRA())
          end if
ERR_NEXT:                
          on error goto 0
        else
        ' Skryj nezadouci listy
          .IsVisible = false
        end if
      end with
    next i

  ' Uloz vybrany list do pdf
    arg(0).Name = "FilterName"
    arg(0).Value = "calc_pdf_Export"
    ThisComponent.storeToURL(ConvertToUrl(sPATH), arg())

  ' Vraceni vsech listu do puvodniho stavu
    for i = 0 to .Count-1
      with .getByIndex(i)
      ' Zobraz puvodni listy
        .IsVisible = bVisible(i)
      ' Nastav puvodni tiskove oblasti
        .PrintAreas = PA(i)
      end with
    next i

  end with
end sub
volání, stejně jako minule, navíc lze volat proceduru bez parametrů:
SheetToPDF
ulozi celý poslední list sesitu do pdf.

A to je pro tentokrát vše. Protože ale vím, že lze výstup do pdf ovlivnit pomocí tiskových parametrů, navic má i filtr pdf své volby, budu experimentovat dále, a možná se tu objeví dodatek, nebude to ale za týden a asi ani za měsíc. Do konce roku bych ale mohl něco vypotit.



<< Předchozí

vgraf2@cbox.cz

23.09.2006

stránky

hlavní stránka

Calc


odkazy

Zahraniční