O FWMSPrinter é um componente da linguagem ADVPL utilizada no ERP Protheus da TOTVS. Ele é responsável por gerar relatórios do tipo RPT e imprimir esses relatórios em uma impressora configurada no sistema operacional.

Esse componente oferece diversas funcionalidades, como a possibilidade de definir o número de cópias a serem impressas, a exibição de uma prévia do relatório antes da impressão e a seleção da impressora a ser utilizada. Além disso, ele permite a criação de relatórios personalizados e a utilização de variáveis para filtrar e exibir informações específicas nos relatórios.

O FWMSPrinter é amplamente utilizado no Protheus para a geração de relatórios de diversos tipos, desde relatórios simples até relatórios mais complexos, com gráficos e outras informações visuais.

Neste exemplo, o componente FWMSPrinter é instanciado usando o método :New(), e em seguida são definidos o nome do relatório a ser impresso, o número de cópias e se deve ser exibida uma prévia do relatório antes da impressão. Por fim, o método :Print() é chamado para imprimir o relatório. Observe que o nome do relatório deve ser um nome válido de relatório RPT do Protheus.

 

Segue um exemplo:

//-----------------------------------------------------------------------
/*/{Protheus.doc} FBGnreWS
Impressao GNRE

@author Fernando Bueno
@since 10/03/2020
@version 1.0 

@return .T.
/*/
//-----------------------------------------------------------------------
User function FBGnreWS(oImpGNRE, cAutoriza, cXmlGnre, cNumCtrl)

local oGnreXml:= nil

local aRetSF6		:= {0,0,0,0,''}

local cCab			:= 'Guia Nacional de Recolhimento de Tributos Estaduais - GNRE'
local cUfFav		:= ''
local cCodRec		:= ''
local cRazEmi		:= ''
local cCnpjCpfEm	:= ''
local cEndEmi		:= ''
local cMunEmi		:= ''
local cUfEmi		:= ''	
local cCepEmi		:= ''	
local cTelEmi		:= ''	
local cDocOrig		:= ''
local cCnpjCpfDs	:= ''
local cMunDest		:= ''
local cPerRef		:= ''
local cParcela		:= ''
local cValPrinc		:= ''
local cConvenio		:= ''
local cProduto		:= ''
local cAtuMon		:= ''
local cInfCompl		:= ''
local cJuros		:= ''
local cMulta		:= ''
local cTotRec		:= ''
local cDtVenc		:= ''
local cCodBar		:= ''
local cCodBar1		:= ''
local cCodBar2		:= ''
local cIdGnre		:= ''
local cDtPagAt		:= ''
local cDtLimite		:= ''

local nX			:= 0
Local nY			:= 0
local nBox1			:= 0
local nBox2			:= 0
local nBox3			:= 0
local nSay1			:= 0
local nSay2			:= 0
local nPosIni		:= 0
local nAtuMon		:= 0
local nJuros		:= 0
local nMulta		:= 0
local nAtuMonF6		:= 0
local nJurosF6		:= 0
local nMultaF6		:= 0
local nTotRecF6		:= 0
local nPosCtrl		:= 0
Local cVersao		:= ""
Local cVide			:= "VIDE DETALHAMENTO"
Local nTotItens		:= 24 // Total de itens que podem ser exibidos em uma única página (múltiplo itens)
Local nPages		:= 0 // Total de páginas - Múltiplos itens
Local cAviso 		:= ""
Local cErro  		:= ""

Private oIdent		:= nil
Private oGnre		:= nil
Private oItem		:= nil
Private aItens		:= {}
Private lMultItens		:= .F.  // Define se a guia possui mais de item e veio da rotina FISA214


//Inicializacao do objeto grafico 
if oImpGNRE == nil
	lPreview := .T.
	oImpGNRE 	:= FWMSPrinter():New('GNRE', IMP_SPOOL)
	oImpGNRE:SetPortrait()
	oImpGNRE:Setup()
endif
	
//Inicializacao da pagina do objeto grafico
oImpGNRE:StartPage()
nHPage := oImpGNRE:nHorzRes()
nHPage *= (300/PixelX)
nHPage -= HMARGEM
nVPage := oImpGNRE:nVertRes()
nVPage *= (300/PixelY)
nVPage -= VBOX

for nX:= 1 to 3 //Gera via Banco # Contribuinte # Contribuinte/Fisco
	cAviso 		:= ""
	cErro  		:= ""
	//Alimentando as variáveis
	if ! empty( cXmlGnre )
		oGnreXml := XmlParser(NoAcentReco(cXmlGnre),"_",@cAviso,@cErro)

		If !Empty(cAviso) .And. !Empty(cErro)
			Help("",1,"Help","Help","Problema na leitura do XML. Impossível efetuar a impressão do relatório",1,0)
			return .f.
		EndIf
		//  Harmonizando as versões 1.00 e 2.00 do tipo 0- Guia Simples na impressão do relatório
		cVersao	:= "1.00"
		oGNRE	:= oGnreXml
		lMultItens		:= .F.
		
		If Type("oGNRE:_gnre:_versaoguia") == "O"
			cVersao	:= oGnreXml:_gnre:_versaoguia:text
		EndIf

		If cVersao == "1.00"
			oGNRE	:= oGnreXml:_gnre
			oItem	:= oGNRE
			oIdent	:= oGNRE:_identgnre
		ElseIf cVersao >= "2.00"
			If lLote
				oGNRE	:= oGnreXml:_gnre:_DADOSGNRE
				// Quando temos vários itens mostramos apenas o primeiro no cabeçalho do relatório, seguindo exemplo da própria Sefaz PE.
				If Type("oGNRE:_ITENSGNRE:_ITEM") == "A"
					aItens	:= oGNRE:_ITENSGNRE:_ITEM
					oItem	:= aItens[1]
				Else
					oItem	:= oGNRE:_ITENSGNRE:_ITEM
				EndIf
				oIdent	:= oItem:_IDENTGNRE
				lMultItens  := Len(aItens) > 1
			Else
				oGNRE	:= oGnreXml:_gnre:_DADOSGNRE
				oItem	:= oGNRE:_ITENSGNRE:_ITEM
				oIdent	:= oItem:_IDENTGNRE			
			EndIf
		EndIf

	else //Tratamento casa haja algum erro no retorno do xml
		Alert('Não há dados para imprimir.')
		return
	endif
	
	// ----------------------------------------------
	// Variáveis de Posicionamento
	// ----------------------------------------------
	nBox1	:= iif(nX==1,0,nBox1 + 20)
	nBox2	:= 000
	nBox3	:= iif(nX==1, 20, nBox1 + 20)
	
	nSay1	:= iif(nX==1, 13, nBox1 + 13)
	nSay2	:= iif(nX==1, 60, nBox2 + 80)

	// ----------------------------------------------
	// BOX: Cabeçalho
	// ----------------------------------------------
	oImpGNRE:Box(nBox1,000,nBox3,450,'-6')	
	oImpGNRE:Say(nSay1, nSay2, cCab, oFont12N)  
			
	// ----------------------------------------------
	// BOX: UF Favorecida
	// ----------------------------------------------
	cUfFav 	:= alltrim( oIdent:_uf:text )
	
	nSay1	:= nBox1 + 8
	nSay1A	:= nBox1 + 16
	
	oImpGNRE:Box(nBox1,450,nBox3,520,'-6')
	oImpGNRE:Say(nSay1, 453, 'UF Favorecida', oFont07N)
	oImpGNRE:Say(nSay1A, 453, cUfFav, oFont08)
	
	// ----------------------------------------------
	// BOX: Código da Receita
	// ----------------------------------------------
	cCodRec	:= alltrim( oIdent:_receita:text )
		
	oImpGNRE:Box(nBox1,520,nBox3,600,'-6')
	oImpGNRE:Say(nSay1, 523, 'Código da Receita', oFont07N) 
	oImpGNRE:Say(nSay1A, 523, cCodRec, oFont08)  
	
	
	// ----------------------------------------------
	// BOX: Dados do Contribuinte Emitente
	// ----------------------------------------------	
	cRazEmi	:= alltrim( oGNRE:_emitente:_nome:text )
	
	//cnpj/cpf/ie
	if ! empty( oGNRE:_emitente:_cnpjcpf:text )
		cCnpjCpfEm	:= transform(oGNRE:_emitente:_cnpjcpf:text,iif(len(oGNRE:_emitente:_cnpjcpf:text)<>14,"@r 999.999.999-99","@r 99.999.999/9999-99"))
	else
		cCnpjCpfEm	:= alltrim( oGNRE:_emitente:_ie:text )
	endif
	
	cEndEmi	:= alltrim( oGNRE:_emitente:_endereco:text )
	cMunEmi	:= alltrim( oGNRE:_emitente:_descmun:text )
	cUfEmi		:= alltrim( oGNRE:_emitente:_uf:text )
	cCepEmi	:= alltrim( oGNRE:_emitente:_cep:text )
	
	cTelEmi	:= alltrim( oGNRE:_emitente:_telefone:text )
	cTelEmi	:= transform(cTelEmi, iif(len(cTelEmi) == 11, '@R (99) 99999-9999', '@R (99) 9999-9999' ))
	
	oImpGNRE:Box(nBox1 + 20,000,nBox3 + 060,450,'-6')	
	oImpGNRE:Say(nBox1 + 28, 0180, 'Dados do Contribuinte Emitente' , oFont07N)
	oImpGNRE:Say(nBox1 + 36, 0005, 'Razão Social:' , oFont07N)
	oImpGNRE:Say(nBox1 + 46, 0005, cRazEmi , oFont08)
	oImpGNRE:Say(nBox1 + 36, 0350, 'CNPJ/CPF/Insc. Est.:' , oFont07N)
	oImpGNRE:Say(nBox1 + 46, 0350, cCnpjCpfEm , oFont08)
	oImpGNRE:Say(nBox1 + 54, 0005, 'Endereço:' , oFont07N)
	oImpGNRE:Say(nBox1 + 54, 0045, cEndEmi , oFont08)
	oImpGNRE:Say(nBox1 + 64, 0005, 'Município:' , oFont07N)
	oImpGNRE:Say(nBox1 + 64, 0045, cMunEmi , oFont08)
	oImpGNRE:Say(nBox1 + 64, 0350, 'UF:' , oFont07N)
	oImpGNRE:Say(nBox1 + 64, 0365, cUfEmi , oFont08)
	oImpGNRE:Say(nBox1 + 74, 0005, 'CEP:' , oFont07N)
	oImpGNRE:Say(nBox1 + 74, 0025, cCepEmi , oFont08)
	oImpGNRE:Say(nBox1 + 74, 0350, 'Telefone:' , oFont07N)
	oImpGNRE:Say(nBox1 + 74, 0385, cTelEmi , oFont08)
		
	// ----------------------------------------------
	// BOX: Número de Controle
	// ----------------------------------------------
	nPosCtrl := iif(len(cNumCtrl)>16,0513,0528)
		
	oImpGNRE:Box(nBox1 + 20,450,nBox3 + 020,600,'-6')
	oImpGNRE:Say(nBox1 + 28, 0453, 'Nº de Controle', oFont07N) 
	oImpGNRE:Say(nBox1 + 36, nPosCtrl, cNumCtrl, oFont08)  
		
	// ----------------------------------------------
	// BOX: Data de Vencimento
	// ----------------------------------------------
	cDtVenc := substr(oIdent:_vencimento:text,7,2)+'/'+ substr(oIdent:_vencimento:text,5,2)+'/'+ substr(oIdent:_vencimento:text,1,4)
		
	oImpGNRE:Box(nBox1 + 40,450, nBox3 + 040,600,'-6')
	oImpGNRE:Say(nBox1 + 48, 0453, 'Data de Vencimento', oFont07N) 
	If lMultItens
		oImpGNRE:Say(nBox1 + 56, 0470 , cVide , oFont07)
	Else		
		oImpGNRE:Say(nBox1 + 56, 0558, cDtVenc, oFont08)
	EndIf
	
	// ----------------------------------------------
	// BOX: Nº Documento de Origem
	// ----------------------------------------------
	cDocOrig := transform(DocOrig(oIdent:_docorig:text),'@R 999999999')
	
	oImpGNRE:Box(nBox1 + 60,450,nBox3 +  060,600,'-6')
	oImpGNRE:Say(nBox1 + 68, 0453, 'Nº Documento de Origem', oFont07N) 
	If lMultItens
		oImpGNRE:Say(nBox1 + 76, 0470 , cVide , oFont07)
	Else		
		oImpGNRE:Say(nBox1 + 76, 0558, cDocOrig, oFont08)
	EndIf
	
	
	
	// ----------------------------------------------
	// BOX: Dados do Destinatário
	// ----------------------------------------------
	//cnpj/cpf/ie
	if ! empty (oItem:_destinatario:_ie:text)
		cCnpjCpfDs	:= alltrim( oItem:_destinatario:_ie:text )	
	else
		cCnpjCpfDs	:= transForm(oItem:_destinatario:_cnpjcpf:text,iif(len(oItem:_destinatario:_cnpjcpf:text)<>14,"@r 999.999.999-99","@r 99.999.999/9999-99")) 
	endif
	
	cMunDest := alltrim( oItem:_destinatario:_descmun:text )
	
	
	oImpGNRE:Box(nBox1 + 080, 000, nBox3 + 090, 450, '-6')	
	oImpGNRE:Say(nBox1 + 088, 0180, 'Dados do Destinatário' , oFont07N)
	oImpGNRE:Say(nBox1 + 096, 0005, 'CNPJ/CPF/Insc. Est.:' , oFont07N)
	oImpGNRE:Say(nBox1 + 096, 0080, cCnpjCpfDs , oFont08)
	oImpGNRE:Say(nBox1 + 106, 0005, 'Município:' , oFont07N)
	oImpGNRE:Say(nBox1 + 106, 0080, cMunDest , oFont08)
	
	
	// ----------------------------------------------
	// BOX: Período de Referência
	// ----------------------------------------------
	cPerRef := alltrim( oItem:_referencia:_mes:text ) + '/' + alltrim( oItem:_referencia:_ano:text )
	
	oImpGNRE:Box(nBox1 + 080,450,nBox3 + 080,530,'-6')
	oImpGNRE:Say(nBox1 + 088, 0453, 'Período de Referência', oFont07N) 
	oImpGNRE:Say(nBox1 + 096, 0500, IIf (lMultItens , '' ,cPerRef ), oFont08) 
	
	
	// ----------------------------------------------
	// BOX: Parcela
	// ----------------------------------------------
	cParcela := alltrim( oItem:_referencia:_parcela:text )
	
	oImpGNRE:Box(nBox1 + 080,530,nBox3 + 080,600,'-6')
	oImpGNRE:Say(nBox1 + 088, 0533, 'Parcela', oFont07N) 
	oImpGNRE:Say(nBox1 + 096, 0588, Iif (lMultItens, '' , cParcela) , oFont08) 
	
	//Tratamento para buscar valores de Atualização Monetária, Juros e Multa da SF6 conforme retorno SEFAZ PE
	//Obs. esta função não atende RJ e ES
	
	if !cUfFav $"RJ#ES"

		If lMultItens
			cIdGnre := alltrim( oGnre:_numerognre:text )
		Else
			cIdGnre := alltrim( oIdent:_numerognre:text )
		EndIf
		aRetSF6:= RetValSF6(cUfFav, cIdGnre , lMultItens )
		
		nAtuMonF6	 	:= aRetSF6[1]
		nJurosF6	 	:= aRetSF6[2]
		nMultaF6	 	:= aRetSF6[3]
		nTotRecF6	 	:= aRetSF6[4]
		cDtLimite	 	:= aRetSF6[5]
		nValPrinc    	:= aRetSF6[6]
	
	endif	

	// ----------------------------------------------
	// BOX: Valor Principal
	// ----------------------------------------------
	cMascara	:= '@e 9,999,999,999,999.99'
	If lLote
		If lMultItens
			cValPrinc	:= PadL( 'R$ '+ alltrim(transform( nValPrinc , cMascara)),len(cMascara))
		Else
			cValPrinc	:= PadL( 'R$ '+ alltrim(transform(val(oItem:_valores:_principal:text), cMascara)),len(cMascara))
		EndIf	
	Else
		cValPrinc	:= PadL( 'R$ '+ alltrim(transform(val(oItem:_valores:_principal:text), cMascara)),len(cMascara))	
	EndIf
	
	oImpGNRE:Box(nBox1 + 100,450,nBox3 + 100,600,'-6')
	oImpGNRE:Say(nBox1 + 108, 0453, 'Valor Principal', oFont07N) 
	oImpGNRE:SayAlign( nBox1 + 108,0500,cValPrinc,oFont08, 96, 19, , 1)
	
	
	// ----------------------------------------------
	// BOX: Reservado a Fiscalização
	// ----------------------------------------------
	cConvenio	:= alltrim( oIdent:_convenio:text )
	cProduto	:= alltrim( oIdent:_produto:text )
	
	oImpGNRE:Box(nBox1 + 110, 000, nBox3 + 130, 450, '-6')	
	oImpGNRE:Say(nBox1 + 118, 0180, 'Reservado a Fiscalização' , oFont07N)
	oImpGNRE:Say(nBox1 + 128, 0005, 'Convênio/Protocolo:' , oFont07N)
	oImpGNRE:Say(nBox1 + 128, 0080, cConvenio , oFont08)
	oImpGNRE:Say(nBox1 + 138, 0005, 'Produto:' , oFont07N)
	oImpGNRE:Say(nBox1 + 138, 0080, cProduto , oFont08) 
	

	// ----------------------------------------------
	// BOX: Atualização Monetária
	// ----------------------------------------------	
	nAtuMon	:=  iif(!cUfFav $"RJ#ES", nAtuMonF6, val(oItem:_valores:_atumonetaria:text))
	cAtuMon	:= PadL( 'R$ '+ alltrim(transform(nAtuMon, cMascara)),len(cMascara))
	
	oImpGNRE:Box(nBox1 + 120,450,nBox3 + 120,600,'-6')
	oImpGNRE:Say(nBox1 + 128, 0453, 'Atualização Monetária', oFont07N) 
	oImpGNRE:SayAlign( nBox1 + 128,0500,cAtuMon,oFont08, 96, 19, , 1)
	
	// ----------------------------------------------
	// BOX: Informações Complementares
	// ----------------------------------------------
	//Função que retorna Informações Complementares
	cInfCompl := RetInfComp( oGnreXml )
	
	//Tratmento para informar documento válido com a data retornada pela SEFAZ como data limite quando for SEFAZ Nacional PE
	cDtPagAt :=  iif(!cUfFav $"RJ#ES", cDtLimite, cDtVenc)
	cDtPagAt :=  cDtVenc
	
	oImpGNRE:Box(nBox1 + 140, 000, nBox3 + 180, 450, '-6')	
	oImpGNRE:Say(nBox1 + 148, 0005, 'Informações Complementares' , oFont07N)
	If lMultItens
		oImpGNRE:Say(nBox1 + 148, 0300, 'GNRE COM MÚLTIPLOS ITENS' , oFont08)
	EndIf
	oImpGNRE:Say(nBox1 + 158, 0005, cInfCompl , oFont08)

	aRemDes := fGetRemDest(oIdent:_chavenf:text)
	cRemNReduz := AllTrim(aRemDes[1])
	cDesNReduz := AllTrim(aRemDes[2])
	cNFE 	   := AllTrim(aRemDes[3])
	oImpGNRE:Say(nBox1 + 182, 0005, cRemNReduz + ', ' + cDesNReduz + ', Número NF-e: ' + cNFE, oFont08)

	If !Empty(cDtPagAt)
		oImpGNRE:Say(nBox1 + 194, 0005, 'Documento Válido para pagamento até ' , oFont08N)
		oImpGNRE:Say(nBox1 + 194, 0200, cDtPagAt , oFont08) 
	EndIf
	
	// ----------------------------------------------
	// BOX: Juros
	// ----------------------------------------------
	nJuros:= iif(!cUfFav $"RJ#ES", nJurosF6, val(oItem:_valores:_juros:text))
	cJuros:= padl( 'R$ '+ alltrim(transform(nJuros, cMascara)),len(cMascara))  
	
	
	oImpGNRE:Box(nBox1 + 140,450,nBox3 + 140,600,'-6')
	oImpGNRE:Say(nBox1 + 148, 0453, 'Juros', oFont07N) 
	oImpGNRE:SayAlign( nBox1 + 148,0500,cJuros,oFont08, 96, 19, , 1)
	
	// ----------------------------------------------
	// BOX: Multa
	// ----------------------------------------------
	nMulta:= iif(!cUfFav $"RJ#ES", nMultaF6, val(oItem:_valores:_multa:text))
	cMulta:= PadL( 'R$ '+ alltrim(transform(nMulta, cMascara)),len(cMascara)) 
		
	oImpGNRE:Box(nBox1 + 160,450,nBox3 + 160,600,'-6')
	oImpGNRE:Say(nBox1 + 168, 0453, 'Multa', oFont07N) 
	oImpGNRE:SayAlign( nBox1 + 168,0500,cMulta,oFont08, 96, 19, , 1)
	
	// ----------------------------------------------
	// BOX: Total a Recolher
	// ----------------------------------------------
	nTotRec:= iif(!cUfFav $"RJ#ES", nTotRecF6, val(oItem:_valores:_total:text))
	cTotRec:= PadL( 'R$ '+ alltrim(transform(nTotRec, cMascara)),len(cMascara))	
		
		
	oImpGNRE:Box(nBox1 + 180,450,nBox3 + 180,600,'-6')
	oImpGNRE:Say(nBox1 + 188, 0453, 'Total a Recolher', oFont07N) 
	oImpGNRE:SayAlign( nBox1 + 188,0500,cTotRec,oFont08, 96, 19, , 1)
	
	
	// ----------------------------------------------
	// BOX: Código de Barras
	// ----------------------------------------------
	cCodBar	:= alltrim(cAutoriza)
	
	
	if len(cCodBar) == 44	
		
		cCodBar1A	:= substr(cCodBar,1,11)
		cCodBar1B	:= substr(cCodBar,12,11)
		cCodBar1C	:= substr(cCodBar,23,11)
		cCodBar1D	:= substr(cCodBar,34,11)
		
		cCodBar1X	:= cCodBar1A + ' '
		cCodBar1X	+= Mod11(cCodBar1A)+ ' '
		cCodBar1X	+= cCodBar1B+ ' '
		cCodBar1X	+= Mod11(cCodBar1B)+ ' '
		cCodBar1X	+= cCodBar1C+ ' '
		cCodBar1X	+= Mod11(cCodBar1C)+ ' '
		cCodBar1X	+= cCodBar1D+ ' '
		cCodBar1X	+= Mod11(cCodBar1D)	
		
		cCodBar1 := cCodBar1X
		
		//Tratamento para tirar os digitos
		cCodBar2	:= alltrim(cCodBar)	
	else 
		cCodBar1 := substr(cCodBar,1,11) + ' '+ substr(cCodBar,12,1) + ' ' + substr(cCodBar,13,11) + ' ' + substr(cCodBar,24,1) + ' '+ substr(cCodBar,25,11) + ' ' +substr(cCodBar,36,1) + ' ' + substr(cCodBar,37,11) + ' ' + substr(cCodBar,48,1)
		
		//Tratamento para tirar os digitos
		cCodBar2	:=  alltrim(substr(cCodBar,1,11) + substr(cCodBar,13,11) + substr(cCodBar,25,11) + substr(cCodBar,37,11) )
	endif
	
	
	oImpGNRE:say(nBox1 + 212, 0055, cCodBar1 , oFont10N)
	nFontSize := 40
	
	//Tratamento posição Código Barras 
	if nX==1
		nPosIni:= 18.5
	elseif nX==2
		nPosIni:= 42
	else
		nPosIni:= 65.5
	endif
	
	oImpGNRE:MSBAR("INT25",nPosIni,1,cCodBar2,oImpGNRE,.F.,Nil,Nil,0.020,0.8,Nil,Nil,"A",.F.)
	
	//Via Boleto
	if nX == 1 
		oImpGNRE:say(nBox1 + 215, 0555, '1ª via - Banco' , oFont07)
	elseif nX == 2
		oImpGNRE:say(nBox1 + 215, 0540, '2ª via - Contribuinte' , oFont07)
	else
		oImpGNRE:say(nBox1 + 215, 0522, '3ª via - Contribuinte/Fisco' , oFont07)
	endif 
	
	//Atribuo a ultima posição no nBox 1 para a via do contribuinte
	
	if nX == 1 .or. nX == 2
	    //linha pontilhada
		oImpGNRE:say(nBox1 + 270, 0000, replicate(' - ',100) , oFont07)
		if nX == 1
			nBox1 := 258
		elseif nX==2
			nBox1 := 535
		endif
	endif
	
next
If lMultItens
	cMascara	:= '@e 9,999,999,999,999.99'
	nPages := CalcPag(Len(aItens),nTotItens)
	For nY := 1 To nPages
		oImpGNRE:StartPage()
		nBox1 := 0
		// BOX Cabeçalho 
		oImpGNRE:Box(nBox1 ,0,034 ,600,'-6')
		oImpGNRE:Say(nBox1 + 10 , 0180, "Guia de Recolhimento de Tributos Estaduais - GNRE", oFont12N)  
		oImpGNRE:Say(nBox1 + 18 , 0230, "Itens da GNRE Múltipla nº " + CIB->CIB_NUMCTR , oFont07N)  
		oImpGNRE:Say(nBox1 + 26 , 0260,  "(Conforme ajuste SNIEF 09/2018)" , oFont07)  
		nBox1 := 34 // Atualiza linha
		// BOX Dados da UF e Data de emissão
		oImpGNRE:Box(nBox1 ,0,49 ,600,'-6')
		oImpGNRE:Say(nBox1 +8 , 020, "UF Favorecida:" , oFont07N)
		oImpGNRE:Say(nBox1 +8 , 0130, cUfFav , oFont07)
		// oImpGNRE:Say(nBox1 + 8, 0340 , "Data/Hora de Emissão" , oFont07N)
		// oImpGNRE:Say(nBox1 + 8, 0420 , "18/09/2018" , oFont07N)
		nBox1 := 49

		//  BOx Idenrtificação do emitente
		oImpGNRE:Box(nBox1 ,0,69 ,600,'-6')	
		oImpGNRE:Say(nBox1 +8  , 020, "Identificação Emitente: " , oFont07N)
		oImpGNRE:Say(nBox1 +8  , 0130, cCnpjCpfEm , oFont07)
		oImpGNRE:Say(nBox1 + 16, 020 , "Razão Social/Nome: " , oFont07N)
		oImpGNRE:Say(nBox1 + 16, 0130 , cRazEmi , oFont07)
		
		nBox1 := 71
		oImpGNRE:Say(nBox1 + 8 , 0300,  "Itens da GNRE" , oFont07N)  
		nBox1 := 82
		oImpGNRE:Box(nBox1 ,0  ,98 ,25,'-6') // Item
		oImpGNRE:Say(nBox1 +8  , 5, "Item" , oFont07N)

		oImpGNRE:Box(nBox1 ,25 ,98 ,250,'-6') // Dados do item
		oImpGNRE:Say(nBox1 +8  , 30, "Dados do Item" , oFont07N)

		oImpGNRE:Box(nBox1 ,250,98 ,310,'-6') // Receita
		oImpGNRE:Say(nBox1 +8  , 255, "Receita" , oFont07N)

		oImpGNRE:Box(nBox1 ,310,98 ,370,'-6') // Valor Principal
		oImpGNRE:Say(nBox1 +8  , 315, "Valor Principal" , oFont07N)

		oImpGNRE:Box(nBox1 ,370,98 ,430,'-6') // Multa e juros
		oImpGNRE:Say(nBox1 +8  , 375, "Multa/Juros" , oFont07N)

		oImpGNRE:Box(nBox1 ,430,98 ,490,'-6') // Valor total
		oImpGNRE:Say(nBox1 +8  , 435, "Valor Total" , oFont07N)

		oImpGNRE:Box(nBox1 ,490,98 ,600,'-6') // Controle UF
		oImpGNRE:Say(nBox1 +8  , 495, "Controle UF" , oFont07N)

		nBox1 += 2// Espaço entre cabeçalho do item e itens
		
		For nX := 1 To Len(aItens)
			nBox1 += 28  // Atualiza linha
			oImpGNRE:Box(nBox1 ,0  ,nBox1 + 28 ,25,'-6') // Item
			oImpGNRE:Say(nBox1 +8  , 5, AllTrim(AllTochar(nX)) , oFont07N)
				
			oImpGNRE:Box(nBox1 ,25 ,nBox1 + 28 ,250,'-6') // Dados do item
			
			oImpGNRE:Say(nBox1 +8  , 30, "Doc. Origem: " + aItens[nX]:_identgnre:_chavenf:Text , oFont07)
			 
			oImpGNRE:Say(nBox1 +16 , 30, "Vencimento: " + substr(aItens[nX]:_identgnre:_vencimento:Text,7,2)+'/'+ substr(aItens[nX]:_identgnre:_vencimento:Text,5,2)+'/'+ substr(aItens[nX]:_identgnre:_vencimento:Text,1,4) , oFont07)

			oImpGNRE:Box(nBox1 ,250,nBox1 + 28 ,310,'-6') // Receita
			oImpGNRE:Say(nBox1 +8  , 255, aItens[nX]:_identgnre:_Receita:Text , oFont07)
			
			// Procure os valores atualizados por guia
			cIdGnre := alltrim( aItens[nX]:_identgnre:_numerognre:Text )
			aRetSF6:= RetValSF6(cUfFav, cIdGnre , .F. )
		
			oImpGNRE:Box(nBox1 ,310,nBox1 + 28 ,370,'-6') // Valor Principal
			oImpGNRE:Say(nBox1 +8  , 312, 'R$ '+ AllTrim(Transform( Val(aItens[nX]:_valores:_principal:text) , cMascara))  , oFont07)

			oImpGNRE:Box(nBox1 ,370,nBox1 + 28 ,430,'-6') // Multa e juros
			oImpGNRE:Say(nBox1 +8  , 372,  'R$ '+ AllTrim(Transform( aRetSF6[2] + aRetSF6[3] + aRetSF6[1]  , cMascara))   , oFont07)

			oImpGNRE:Box(nBox1 ,430,nBox1 + 28 ,490,'-6') // Valor total
			oImpGNRE:Say(nBox1 +8  , 432, 'R$ '+ AllTrim(Transform( aRetSF6[4] , cMascara))   , oFont07)

			oImpGNRE:Box(nBox1 ,490,nBox1 + 28 ,600,'-6') // Controle UF
			oImpGNRE:Say(nBox1 +8  , 495, "" , oFont07)

		Next nX 
		nBox1 += 38
		oImpGNRE:Say(nBox1 ,0420 , "Total da GNRE (R$)" , oFont07N)
		oImpGNRE:Box(nBox1 -8 ,490  ,nBox1 + 2 ,600,'-6') // Item
		oImpGNRE:Say(nBox1 , 500 , PadL( 'R$ '+ AllTrim(Transform( nTotRecF6 , cMascara)),len(cMascara))   , oFont07N)

		oImpGNRE:Say(850 ,0550 , "Página " +  AllTrim(AllToChar(nY)) + "/"  + AllTrim(AllToChar(nPages)) + ".", oFont07N)
		oImpGNRE:EndPage()
	Next nY	
EndIf

return(.T.)

Em conclusão, o FWMSPrinter é um componente fundamental para a geração de relatórios no Protheus, oferecendo diversas funcionalidades para a impressão de relatórios RPT de maneira fácil e personalizada. Com sua utilização, é possível gerar relatórios simples ou complexos, personalizados de acordo com as necessidades de cada empresa.

Além disso, o FWMSPrinter permite a utilização de variáveis para a filtragem e exibição de informações específicas nos relatórios, tornando-os mais precisos e relevantes para a tomada de decisões.

Por essas razões, é fundamental que os desenvolvedores do Protheus conheçam bem as funcionalidades do FWMSPrinter e saibam como utilizá-lo de maneira eficiente para a geração de relatórios precisos e personalizados. Com isso, é possível maximizar o potencial do ERP Protheus e obter melhores resultados para a empresa como um todo.

Espero que ajude!