Gerar PDF com Senha via AdvPL

No artigo de hoje, vamos demonstrar duas formas de como gerar um arquivo PDF com senha via AdvPL.

Pessoal, apenas contextualizando, montei o fonte abaixo (e deu um trabalhinho pra montar rs), após eu acabar o exemplo, eis que eu vi que na próxima LIB (20230807), esse recurso de colocar a senha será nativo.

Então abaixo vou demonstrar as duas formas, usando a LIB nova, e usando de forma manual.

Forma 1 – Usando a LIB 20230807

Se você estiver usando a LIB superior a agosto de 2023, basta você usar o método SetPassword para definir a senha do arquivo pdf.

Então logo após instanciar a impressão em PDF, você aciona o método. Supondo que a senha seja tst123, ela ficará da seguinte forma:

//Instancia o FWMSPrinter com o tipo PDF (device IMP_PDF)
oPrintPvt := FWMSPrinter():New(cArquivo, IMP_PDF, .F., /*cPathInServer*/, .T., /*lTReport*/, @oPrintPvt, /*cPrinter*/, /*lServer*/, /*lParam10*/, /*lRaw*/,.T.)

//Define a senha do arquivo pdf
oPrintPvt:SetPassword("tst123")

Forma 2 – Usando manualmente o printer.exe para gerar o pdf com senha

Para esse processo, nós criamos o relatório normalmente, só que no fim dele, nós acionamos o printer.exe para gerar o arquivo pdf (através do arquivo .rel) já com a senha.

 

Então segue algumas observações:

  • lViewPdf tem que estar como .F.
  • Tem que ser acionado o método preview para gerar o arquivo .rel
  • Ao invés do método Print() usar o ShellExecute() acionando a printer.exe
  • Foi tentado utilizar a File2Printer() mas ele não gerava o arquivo, então por isso acionamos manualmente com ShellExecute

 

Dito as observações acima, os pontos importantes no fonte, são os seguintes trechos:

  • Linhas 120 a 138: Aqui iremos criar nossa pasta caso não exista (estamos usando C:\spool\) e vamos instanciar nosso FWMSPrinter
  • Linhas 174 a 189: Aqui iremos dar um preview (para gerar o .rel), ai buscamos o nome do arquivo. Depois pegamos a pasta do SmartClient que esta executando e definimos uma senha para o pdf (variável cSenha). Por fim, acionamos o ShellExecute, aguardando 5 segundos e depois apagamos o .rel e abrimos a pasta com o pdf gerado.

 

Abaixo um gif demonstrando a abertura do arquivo:

Exemplo de pdf com senha gerado via AdvPL

 

E abaixo o código fonte completo:

//Bibliotecas
#Include "Totvs.ch"
#Include "TopConn.ch"
#Include "RPTDef.ch"
#Include "FWPrintSetup.ch"

//Alinhamentos
#Define PAD_LEFT    0
#Define PAD_RIGHT   1
#Define PAD_CENTER  2

//Cor(es)
Static nCorCinza := RGB(110, 110, 110)
Static nCorLinha := RGB(148, 255, 180)

/*/{Protheus.doc} User Function zPdfTst
Exemplo de um relatório com FWMSPrinter
@author Atilio
@since 12/06/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see https://tdn.totvs.com/display/public/framework/FWMsPrinter e http://autumncodemaker.com
/*/

User Function zPdfTst()
	Local aArea := FWGetArea()
	Local aPergs   := {}
	Local xPar0 := Space(15)
	Local xPar1 := StrTran(xPar0, ' ', 'Z')
	
	//Adicionando os parametros do ParamBox
	aAdd(aPergs, {1, "Produto De", xPar0,  "", ".T.", "SB1", ".T.", 80,  .F.})
	aAdd(aPergs, {1, "Produto Até", xPar1,  "", ".T.", "SB1", ".T.", 80,  .T.})
	
	//Se a pergunta for confirma, cria o relatorio
	If ParamBox(aPergs, "Informe os parametros", , , , , , , , , .F., .F.)
		Processa({|| fImprime()})
	EndIf
	
	FWRestArea(aArea)
Return

/*/{Protheus.doc} fImprime
Faz a impressão do relatório zPdfTst
@author Atilio
@since 12/06/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/

Static Function fImprime()
    Local aArea        := GetArea()
    Local nTotAux      := 0
    Local nAtuAux      := 0
    Local cQryAux      := ''
    Local cArquiRel    := ''
    Local cArquivo     := 'zPdfTst'+RetCodUsr()+'_' + dToS(Date()) + '_' + StrTran(Time(), ':', '-') + '.pdf'
    Local cPasta       := "C:\spool\"
    Local cPastaSC     := ""
    Local cSenha       := ""
    Private oPrintPvt
    Private oBrushLin  := TBrush():New(,nCorLinha)
    Private cHoraEx    := Time()
    Private nPagAtu    := 1
    Private cLogoEmp   := fLogoEmp()
    //Linhas e colunas
    Private nLinAtu    := 0
    Private nLinFin    := 800
    Private nColIni    := 010
    Private nColFin    := 580
    Private nColMeio   := (nColFin-nColIni)/2
    //Colunas dos relatorio
    Private nColDad1    := nColIni
    Private nColDad2    := nColIni + 50
    Private nColDad3    := nColIni + 150
    Private nColDad4    := nColIni + 200
    Private nColDad5    := nColIni + 300
    //Declarando as fontes
    Private cNomeFont  := 'Arial'
    Private oFontDet   := TFont():New(cNomeFont, 9, -11, .T., .F., 5, .T., 5, .T., .F.)
    Private oFontDetN  := TFont():New(cNomeFont, 9, -13, .T., .T., 5, .T., 5, .T., .F.)
    Private oFontRod   := TFont():New(cNomeFont, 9, -8,  .T., .F., 5, .T., 5, .T., .F.)
    Private oFontMin   := TFont():New(cNomeFont, 9, -7,  .T., .F., 5, .T., 5, .T., .F.)
    Private oFontTit   := TFont():New(cNomeFont, 9, -15, .T., .T., 5, .T., 5, .T., .F.)
     
    //Monta a consulta de dados
    cQryAux += "SELECT "		+ CRLF
    cQryAux += " B1_COD, "		+ CRLF
    cQryAux += " B1_DESC, "		+ CRLF
    cQryAux += " B1_GRUPO, "		+ CRLF
    cQryAux += " BM_DESC "		+ CRLF
    cQryAux += "FROM "		+ CRLF
    cQryAux += " SB1990 SB1 "		+ CRLF
    cQryAux += " INNER JOIN SBM990 SBM ON ( "		+ CRLF
    cQryAux += " BM_FILIAL = '01' "		+ CRLF
    cQryAux += " AND BM_GRUPO = B1_GRUPO "		+ CRLF
    cQryAux += " AND SBM.D_E_L_E_T_ = ' ' "		+ CRLF
    cQryAux += " ) "		+ CRLF
    cQryAux += "WHERE "		+ CRLF
    cQryAux += " B1_FILIAL = '' "		+ CRLF
    cQryAux += " AND B1_COD >= '" + MV_PAR01 + "' "		+ CRLF
    cQryAux += " AND B1_COD <= '" + MV_PAR02 + "' "		+ CRLF
    cQryAux += " AND B1_MSBLQL != '1' "		+ CRLF
    cQryAux += " AND SB1.D_E_L_E_T_ = ' '"		+ CRLF
    PLSQuery(cQryAux, 'QRY_AUX')
 
    //Define o tamanho da régua
    DbSelectArea('QRY_AUX')
    QRY_AUX->(DbGoTop())
    Count to nTotAux
    ProcRegua(nTotAux)
    QRY_AUX->(DbGoTop())
     
    //Somente se tiver dados
    If ! QRY_AUX->(EoF())
        //Cria a pasta
        If ! ExistDir(cPasta)
            MakeDir(cPasta)
        EndIf

        //Criando o objeto de impressao
        oPrintPvt := FWMSPrinter():New(;
            cArquivo,; // cFilePrinter
            IMP_PDF,;  // nDevice
            .F.,;      // lAdjustToLegacy
            cPasta,;   // cPathInServer
            .T.,;      // lDisabeSetup
            ,;         // lTReport
            ,;         // oPrintSetup
            ,;         // cPrinter
            .F.,;      // lServer
            .F.,;      // lParam10
            ,;         // lRaw
            .F.;       // lViewPDF
        )
        oPrintPvt:SetResolution(72)
        oPrintPvt:SetPortrait()
        oPrintPvt:SetPaperSize(DMPAPER_A4)
        oPrintPvt:SetMargin(0, 0, 0, 0)
 
        //Imprime os dados
        fImpCab()
        While ! QRY_AUX->(EoF())
            nAtuAux++
            IncProc('Imprimindo registro ' + cValToChar(nAtuAux) + ' de ' + cValToChar(nTotAux) + '...')
 
            //Se atingiu o limite, quebra de pagina
            fQuebra()
             
            //Faz o zebrado ao fundo
            If nAtuAux % 2 == 0
                oPrintPvt:FillRect({nLinAtu - 2, nColIni, nLinAtu + 12, nColFin}, oBrushLin)
            EndIf
 
            //Imprime a linha atual
            oPrintPvt:SayAlign(nLinAtu, nColDad1, Alltrim(QRY_AUX->B1_COD), oFontDet, 50, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
            oPrintPvt:SayAlign(nLinAtu, nColDad2, Alltrim(QRY_AUX->B1_DESC), oFontDetN, 100, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
            oPrintPvt:SayAlign(nLinAtu, nColDad3, Alltrim(QRY_AUX->B1_GRUPO), oFontDet, 50, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
            oPrintPvt:SayAlign(nLinAtu, nColDad4, Alltrim(QRY_AUX->BM_DESC), oFontDet, 100, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
 
            nLinAtu += 15
            oPrintPvt:Line(nLinAtu-3, nColIni, nLinAtu-3, nColFin, nCorCinza)
 
            //Se atingiu o limite, quebra de pagina
            fQuebra()
             
            QRY_AUX->(DbSkip())
        EndDo
        fImpRod()
        
        //Aciona a geração do arquivo .rel
        oPrintPvt:Preview()

        //Pega o nome do arquivo .rel
        cArquiRel := oPrintPvt:cFilePrint

        //Pega a pasta do SmartClient e define uma senha que será usada para abrir o PDF
        cPastaSC := GetClientDir()
        cSenha   := "tst123"

        //Aciona o printer.exe para gerar do .rel para .pdf com senha
        ShellExecute("OPEN", cPastaSC + "printer.exe", cArquiRel + " PDF_WITH_PASSWORD " + cSenha, cPasta, 1)

        //Agora, apaga o arquivo .rel, limpa o objeto e abre a pasta com o PDF gerado
        Sleep(5000)
        FErase(cArquiRel)
        FreeObj(oPrintPvt)
        ShellExecute("open", "explorer.exe", cPasta, "C:\", 1)

    Else
        MsgStop('Não foi encontrado informações com os parâmetros informados!', 'Atenção')
    EndIf
    QRY_AUX->(DbCloseArea())
     
    RestArea(aArea)
Return

/*/{Protheus.doc} fLogoEmp
Função que retorna o logo da empresa conforme configuração da DANFE
@author Atilio
@since 12/06/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/

Static Function fLogoEmp()
    Local cGrpCompany := AllTrim(FWGrpCompany())
    Local cCodEmpGrp  := AllTrim(FWCodEmp())
    Local cUnitGrp    := AllTrim(FWUnitBusiness())
    Local cFilGrp     := AllTrim(FWFilial())
    Local cLogo       := ''
    Local cCamFim     := GetTempPath()
    Local cStart      := GetSrvProfString('Startpath', '')

    //Se tiver filiais por grupo de empresas
    If !Empty(cUnitGrp)
        cDescLogo	:= cGrpCompany + cCodEmpGrp + cUnitGrp + cFilGrp
        
    //Senão, será apenas, empresa + filial
    Else
        cDescLogo	:= cEmpAnt + cFilAnt
    EndIf
    
    //Pega a imagem
    cLogo := cStart + 'DANFE' + cDescLogo + '.BMP'
    
    //Se o arquivo não existir, pega apenas o da empresa, desconsiderando a filial
    If !File(cLogo)
        cLogo	:= cStart + 'DANFE' + cEmpAnt + '.BMP'
    EndIf
    
    //Copia para a temporária do s.o.
    CpyS2T(cLogo, cCamFim)
    cLogo := cCamFim + StrTran(cLogo, cStart, '')
    
    //Se o arquivo não existir na temporária, espera meio segundo para terminar a cópia
    If !File(cLogo)
        Sleep(500)
    EndIf
Return cLogo

/*/{Protheus.doc} fImpCab
Função que imprime o cabeçalho do relatório
@author Atilio
@since 12/06/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/

Static Function fImpCab()
    Local cTexto   := ''
    Local nLinCab  := 015
     
    //Iniciando Pagina
    oPrintPvt:StartPage()
    
    //Imprime o logo
    If File(cLogoEmp)
        oPrintPvt:SayBitmap(005, nColIni, cLogoEmp, 030, 030)
    EndIf
     
    //Cabecalho
    cTexto := 'Produtos e Grupos'
    oPrintPvt:SayAlign(nLinCab, nColMeio-200, cTexto, oFontTit, 400, 20, , PAD_CENTER, )
     
    //Linha Separatoria
    nLinCab += 020
    oPrintPvt:Line(nLinCab,   nColIni, nLinCab,   nColFin)
     
    //Atualizando a linha inicial do relatorio
    nLinAtu := nLinCab + 5
    
    If nPagAtu == 1
        //Imprimindo os parâmetros
        oPrintPvt:SayAlign(nLinAtu, nColIni, 'Produto De', oFontDetN, 200, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
        oPrintPvt:SayAlign(nLinAtu, nColIni+200, MV_PAR01, oFontDet, 200, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
        nLinAtu += 15
        oPrintPvt:SayAlign(nLinAtu, nColIni, 'Produto Até', oFontDetN, 200, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
        oPrintPvt:SayAlign(nLinAtu, nColIni+200, MV_PAR02, oFontDet, 200, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
        nLinAtu += 15
        oPrintPvt:Line(nLinAtu-3, nColIni, nLinAtu-3, nColFin, nCorCinza)
        nLinAtu += 5
    EndIf
    
    oPrintPvt:SayAlign(nLinAtu, nColDad1, 'Produto', oFontMin, 50, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
    oPrintPvt:SayAlign(nLinAtu, nColDad2, 'Descrição', oFontMin, 100, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
    oPrintPvt:SayAlign(nLinAtu, nColDad3, 'Grupo', oFontMin, 50, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
    oPrintPvt:SayAlign(nLinAtu, nColDad4, 'Grp. Descrição', oFontMin, 100, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/)
    nLinAtu += 15
Return

/*/{Protheus.doc} fImpRod
Função que imprime o rodapé e encerra a página
@author Atilio
@since 12/06/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/

Static Function fImpRod()
    Local nLinRod:= nLinFin
    Local cTexto := ''
 
    //Linha Separatoria
    oPrintPvt:Line(nLinRod,   nColIni, nLinRod,   nColFin)
    nLinRod += 3
     
    //Dados da Esquerda
    cTexto := dToC(dDataBase) + '     ' + cHoraEx + '     ' + FunName() + ' (zPdfTst)     ' + UsrRetName(RetCodUsr())
    oPrintPvt:SayAlign(nLinRod, nColIni, cTexto, oFontRod, 500, 10, , PAD_LEFT, )
     
    //Direita
    cTexto := 'Pagina '+cValToChar(nPagAtu)
    oPrintPvt:SayAlign(nLinRod, nColFin-40, cTexto, oFontRod, 040, 10, , PAD_RIGHT, )
     
    //Finalizando a pagina e somando mais um
    oPrintPvt:EndPage()
    nPagAtu++
Return

/*/{Protheus.doc} fQuebra
Função que valida se a linha esta próxima do final, se sim quebra a página
@author Atilio
@since 12/06/2023
@version 1.0
@type function
@obs Codigo gerado automaticamente pelo Autumn Code Maker
@see http://autumncodemaker.com
/*/

Static Function fQuebra()
    If nLinAtu >= nLinFin-10
        fImpRod()
        fImpCab()
    EndIf
Return

Referências:

 

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

2 Responses

  1. Súlivan Simões disse:

    Que top hein. Chique demais!

Deixe uma resposta

Terminal de Informação