quarta-feira, 12 de janeiro de 2011

Formatando tags HTML com o Delphi

Está dica ensina como formatar comandos em HTML dentro de um RichEdit.

procedure HTMLSyntax(RichEdit: TRichEdit; TextCol, TagCol, DopCol: TColor);
var
     i, iDop: Integer;
     s: string;
     Col: TColor;
     isTag, isDop: Boolean;
begin
    iDop := 0;
    isDop := False;
    isTag := False;
    Col := TextCol;
    RichEdit.SetFocus;

    for i := 0 to Length(RichEdit.Text) do begin
      RichEdit.SelStart := i;
      RichEdit.SelLength := 1;
      s := RichEdit.SelText;
       
       if (s = '<') or (s = '{') then isTag := True;

       if isTag then
  
       if (s = '"') then

       if not isDop then begin
         iDop := 1;
         isDop := True;
       end
       else
         isDop := False;

       if isTag then

       if isDop then begin
            if iDop <> 1 then Col := DopCol;
       end
       else
        Col := TagCol
      else
        Col := TextCol;
        RichEdit.SelAttributes.Color := Col;
        iDop := 0;
          if (s = '>') or (s = '}') then isTag := False;
end;

   RichEdit.SelLength := 0;
end;

Volte ao formulário principal e no evento onclick do Button insira o seguinte código:
procedure TForm1.Button1Click(Sender: TObject);

begin
    RichEdit1.Lines.BeginUpdate;
    HTMLSyntax(RichEdit1, clBlue, clRed, clGreen);
    RichEdit1.Lines.EndUpdate;
end;

Substituindo um arquivo INI por um documento XML.

Este código mostra como usar TXMLDocument para salvar e restaurar configurações em um documento XML. O método publico trabalha como um TIniFile. O código não precisa ser comentado porque é auto explicativo e pequeno. Foi testado apenas no Delphi 7.


unit uCiaXml;

interface

uses
Forms, SysUtils, Windows, XmlIntf, XMLDoc;

type
TXMLConfig = class
private
FModified: Boolean;
FFileName: string;
FXMLDoc: TXMLDocument;
FBackup: Boolean;
function GetVersion: string;
public
constructor Create(const FileName: string); overload;
constructor Create; overload;
destructor Destroy; override;
procedure Save;
function ReadString(const Section, Key, default: string): string;
procedure WriteString(const Section, Key, Value: string);
function ReadInteger(const Section, Key: string; default: Integer): Integer;
procedure WriteInteger(const Section, Key: string; Value: Integer);
function ReadBoolean(const Section, Key: string; default: Boolean): Boolean;
procedure WriteBoolean(const Section, Key: string; Value: Boolean);
property Backup: Boolean read FBackup write FBackup;
property Version: string read GetVersion;
end;

implementation

{ TXMLConfig }

constructor TXMLConfig.Create(const FileName: string);
begin
inherited Create;
FBackup := True;
FFileName := FileName;
FXMLDoc := TXMLDocument.Create(Application);
FXMLDoc.Options := [doNodeAutoIndent];
if FileExists(FFileName) then
FXMLDoc.LoadFromFile(FFileName)
else
begin
FXMLDoc.Active := True;
FXMLDoc.AddChild('Configuration');
end;
end;

constructor TXMLConfig.Create;
begin
Create(ChangeFileExt(Application.Exename, '_cfg.xml'));
end;

destructor TXMLConfig.Destroy;
begin
Save;
FXMLDoc.Destroy;
inherited;
end;

function TXMLConfig.GetVersion: string;
begin
Result := '1.00';
end;

function TXMLConfig.ReadBoolean(const Section, Key: string; default: Boolean): Boolean;
begin
Result := Boolean(ReadInteger(Section, Key, Integer(default)));
end;

function TXMLConfig.ReadInteger(const Section, Key: string; default: Integer): Integer;
begin
Result := StrToInt(ReadString(Section, Key, IntToStr(default)));
end;

function TXMLConfig.ReadString(const Section, Key, default: string): string;
var
Node: IXMLNode;
begin
Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
if Assigned(Node) and Node.HasAttribute(Key) then
Result := Node.Attributes[Key]
else
Result := default;
end;

procedure TXMLConfig.Save;
begin
if not FModified then
Exit;
if FBackup then

CopyFile(PChar(FFileName), PChar(FFileName + '.bak'), False);
FXMLDoc.SaveToFile(FFileName);
FModified := False;
end;

procedure TXMLConfig.WriteBoolean(const Section, Key: string; Value: Boolean);
begin
WriteInteger(Section, Key, Integer(Value));
end;

procedure TXMLConfig.WriteInteger(const Section, Key: string; Value: Integer);
begin
WriteString(Section, Key, IntToStr(Value));
end;

procedure TXMLConfig.WriteString(const Section, Key, Value: string);
var
Node: IXMLNode;
begin
if ReadString(Section, Key, '') = Value then
Exit;
Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
if not Assigned(Node) then
Node := FXMLDoc.DocumentElement.AddChild(Section);
Node.Attributes[Key] := Value;
FModified := True;
end;

end.

Download de arquivos na WEB

Esta dica tem por objetivo mostrar como é fácil fazer o download de arquivos na WEB.

Declare na cláusula uses: URLMon

Esta função é responsável pelo download do arquivo na WEB.

function DownloadFile(Source, Dest: string): Boolean;
begin
try
   Result := UrlDownloadToFile(nil, PChar(source), PChar(Dest), 0, nil) = 0;
except
   Result := False;
end;
end;

Insira no evento OnClick de um botão o seguinte código:

procedure TForm1.Button1Click(Sender: TObject);
begin
if DownloadFile ('http://www.dicasdelphi.xpg.com.br/downloads/DicasDelphi.zip', 'c:\windows\desktop\dicasdelphi.zip') then
   ShowMessage('Download Concluído!')
else
   ShowMessage('Falha ao fazer o download!!')
end;

Enviando email com Delphi pelo componente NMSMTP

Nesta dica a seguir vamos enviar um email utilizando o componente NMSMTP do Delphi.

Crie um novo projeto e insira um componente do tipo TNMSMTP na aba FastNet da VCL do Delphi

Adicione ao formulário um objeto Button e coloque o codigo a seguir no evento OnClick do objeto.

procedure TForm1.Button1Click(Sender: TObject);
begin
NMSMTP1.Host := 'servidoremailsmtp.com';
NMSMTP1.UserID := 'nomedousuario'; // Nome do Usuário
NMSMTP1.Connect; // Conecta no servidor smtp

NMSMTP1.PostMessage.FromAddress := 'remetente@seudominio.com';
NMSMTP1.PostMessage.ToAddress.Text := 'destino@dominio.com';
NMSMTP1.PostMessage.Body.Text := 'Coloque aqui sua mensagem';
NMSMTP1.PostMessage.Subject := 'Assunto do Email';
NMSMTP1.SendMail; // Envia o email
end;

Com poucas linhas de código é possivel enviar um email pelo delhi através do componente NMSMTP.
Se você quiser sofisticar sua aplicação pode faze-la buscar valores do e-mail, corpo da mensagem e assunto de um banco de dados ou objetos tipo edit.

Abrir automaticamente seu navegador padrão e carregar a pagina determinada pelo link

1º Declare o procedure na seção PUBLIC da unit. 
   procedure JumpTo(const aAdress: String); 

2º Coloque a cláusula ShellAPI na uses no início da unit. 

procedure TForm1.JumpTo(const aAdress: String); 
var 
       buffer: String; 
begin 
       buffer := 'http://' + aAdress; 
       ShellExecute(Application.Handle, nil, PChar(buffer), nil, nil, SW_SHOWNORMAL); 
end; 

procedure TForm1.Label1Click(Sender: TObject); 
begin 
         JumpTo('www.geocities.com/SiliconValley/Way/1497'); 
end; 

terça-feira, 11 de janeiro de 2011

Imprimir com precisão Milimétrica

O objeto Canvas que está na classe Printer é uma ferramenta que ajuda muito a imprimir qualquer tipo de dados,
sejam eles texto ou gráficos. O problema é que a largura e a altura são determinadas em pixels, e esses valores
variam de acordo com a resolução da impressora. Para converter de milímetros para pixels, use as funções abaixo,
sendo que MMtoPixelX é para a resolução horizontal e MMtoPixelY é para a resolução vertical (porque na
impressora é possível uma resolução como 1440x720 dpi - 1440 dpi para a horizontal e 720 dpi para a vertical, por
exemplo):

function MMtoPixelX (MM : Integer) : Longint;
var
mmPointX : Real;
PageSize, OffSetUL : TPoint;
begin
mmPointX := Printer.PageWidth / GetDeviceCaps(Printer.Handle,HORZSIZE);


Escape (Printer.Handle,GETPRINTINGOFFSET,0,nil,@OffSetUL);
Escape (Printer.Handle,GETPHYSPAGESIZE,0,nil,@PageSize);
if MM > 0 then
Result := round ((MM * mmPointX) - OffSetUL.X)
else
Result := round (MM * mmPointX);
end;

function MMtoPixelY (MM : Integer) : Longint;
var
mmPointY : Real;
PageSize, OffSetUL : TPoint;
begin
mmPointY := Printer.PageHeight /
GetDeviceCaps(Printer.Handle,VERTSIZE);
Escape (Printer.Handle,GETPRINTINGOFFSET,0,nil,@OffSetUL);
Escape (Printer.Handle,GETPHYSPAGESIZE,0,nil,@PageSize);
if MM > 0 then
Result := round ((MM * mmPointY) - OffSetUL.Y)
else
Result := round (MM * mmPointY);
end;

Imprimir texto justificado na lx-300

A impressora Epson LX-300 dispõe de um comando que justifica o texto. Este recurso é interessante, pois com ele podemos continuar a enviar os comandos de formatação de caracteres como condensado, negrito, italico, expandido, etc.

Para o exemplo abaixo:
- Coloque um botão no form;
- Altere o evento OnClick deste botão como abaixo:

procedure TForm1.Button1Click(Sender: TObject);
const
cJustif = #27#97#51;
cEject = #12;

{ Tamanho da fonte }
c10cpi = #18;
c12cpi = #27#77;
c17cpi = #15;
cIExpandido = #14;
cFExpandido = #20;
{ Formatação da fonte }
cINegrito = #27#71;
cFNegrito = #27#72;
cIItalico = #27#52;
cFItalico = #27#53;
var
Texto: string;
F: TextFile;
begin
Texto := c10cpi +
'Este e um teste para impressora Epson LX 300. ' +
'O objetivo e imprimir texto justificado sem deixar ' +
'de usar formatacao, tais como: ' +
cINegrito + 'Negrito, ' + cFNegrito +
cIItalico + 'Italico, ' + cFItalico +
c17cpi + 'Condensado (17cpi), ' + c10cpi +
c12cpi + '12 cpi, ' + c10cpi +
cIExpandido + 'Expandido.' + cFExpandido +
' Este e apenas um exemplo, mas voce podera adapta-lo ' +
'a sua realidade conforme a necessidade.';

AssignFile(F, 'LPT1');
Rewrite(F);
try
WriteLn(F, cJustif, Texto);
WriteLn(F, cEject);
finally
CloseFile(F);
end;
end;

Trocar impressora padrão do Windows

Uma dúvida muito frequente em nossos mails são referentes a troca de impressoras em determinados relatórios. A rotina que apresentamos a seguir realiza essa troca:

procedure TForm1.FormShow(Sender: TObject);
var
i: integer;
begin
// Limpa a lista de impressoras mostradas
ListBoc1.Items.Clear;
// Atualiza listbox com nome das impressoras
for i := 1 to Printer.Printers.Count do
ListBox1.Items.Add(Printers[i - 1]);
end;
Para selecionar uma determinada impressora, basta atribuir um inteiro à Printer.PrinterIndex, como você verá a
seguir:
Printer.PrinterIndex := ListBox1.ItemIndex;

segunda-feira, 10 de janeiro de 2011

Enviando caracteres diretamente ao buffer da impressora

Ao trabalharmos com impressão, em certos casos desejamos alterar o comportamento da impressora.

Algumas opções, principalmente em impressoras matriciais, são obtidas através do envio dos chamados "códigos de escape" para a impressora (por exemplo, alterar espaçamento entre as linhas (#45), tipo de fonte (#18, #23), etc). Em versões 16-bit do Windows, isso não era complicado, mas agora, nas versões 32-bit, o acesso direto ao hardware não é mais possível.

Portanto, para enviarmos caracteres diretamente a impressora, devemos utilizar o "escape" chamado "PASSTHROUGH" do Windows e enviarmos a informação desejada diretamente.

Na documentação do Win32 SDK este escape é dado como obsoleto, mas enquanto utilizarmos impressoras matriciais que necessitem de "códigos escape" para certas funcionalidades, ele será necessário.

Ao utilizar impressoras Postscript tenha cuidado, pois nem sempre esta técnica irá funcionar. Em impressoras matriciais, você pode enviar qualquer tipo de caracteres que achar necessário.

Abaixo segue um código exemplificando o envio de uma string qualquer diretamente ao buffer da impressora:

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TForm1 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

uses Printers;

type
{ Tipo requerido pelo PASSTHROUGH }
TBufferImpressora = record
TamanhoBuffer: Word;
Buffer: array [0..255] of Char;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
       Buff: TBufferImpressora;
       TestePasstrough: Integer;
       strFoo: string;
begin
{ Primeiro devemos checar se o "escape" PASSTHROUGH é suportado. Para isso, executamos a função "Escape" passando o QUERYESCSUPPORT. Caso o driver suporte, ela irá retornar um valor maior que 0 }
       TestePasstrough := PASSTHROUGH;
       if Escape(Printer.Handle, QUERYESCSUPPORT, SizeOf(PASSTHROUGH), @TestePasstrough, nil) > 0 then
       begin
                   { Inicializamos o driver }
                   Printer.BeginDoc;
                   { Informação qualquer a enviar diretamente para a impressora }
                   strFoo := 'Passthrough string';
                   { Copia da string para a estrutura }
                   StrPCopy(Buff.Buffer, strFoo);
                   { Indicamos o tamanho da informação}
                   Buff.TamanhoBuffer := StrLen(Buff.Buffer);
                   { Enviamos o "escape" }
                   Escape(Printer.Canvas.Handle, PASSTHROUGH, 0, @Buff,nil);
                  { Descarregamos... }
                  Printer.EndDoc;
       end;
end;

end.

Enviando informações direto para a impressora

Muitas vezes torna-se necessário, ou até mesmo, imprescindível que você envie informações diretamente para a impressora, uma vez que a utilização da impressão típica do Windows é um pouco demorada e o uso do driver Genérico/Somente Texto não é muito confiável.

Uma boa solução para enviar informações diretamente para a impressora é usar o seguinte código:

Procedure TForm1.Button1Click(Sender: Object);
var
         Imp: TextFile;
begin
         AssignFile(Imp, 'LPT1');
         Rewrite(Imp);
         Write(Imp, 'Isto vai sair na impressora');
         CloseFile(Imp);
end;

Desta forma será possível, inclusive, utilizar os códigos de configuração da impressora. Para a impressora padrão Epson, por exemplo, você poderia utilizar algo assim:

Write(Imp, #27#69 + 'Teste' + #27#70); { impressão em negrito }
Write(Imp, #15 + 'Teste' + #18); { impressão no modo condensado }
Write(Imp, #12); { salto de página }

Conhecendo os Fundamentos do ICMS ST (Substituição Tributária)


Olá Amigos! Resolvi escrever sobre um assunto complicado para os programadores, principalmente os iniciantes! Lembro-me que quando comecei, logo de cara tinha que fazer um cálculo de Substituição Tributária em um software de emissão de notas fiscais
Os problemas apareceram quando resolvi perguntar ao contador do cliente. Aí sim descobri que quase ninguém domina esse assunto! Felizmente isso são águas passadas. Hoje resolvi escrever esse artigo para ajudar aqueles que estão passando por algo parecido.

Vou utilizar algumas nomenclaturas comuns para quem já está trabalhando com NFe (Nota Fiscal Eletrônica).

Base de cálculo (BC)

A Lei Complementar nº 87/96 em seu artigo 8º, ao tratar do regime de sujeição passiva por substituição, determina que a base de cálculo será o valor correspondente ao preço de venda a consumidor acrescido do valor do frete, IPI e demais despesas debitadas ao estabelecimento destinatário, bem como a parcela resultante da aplicação (sobre esse total) do percentual de valor agregado (margem de lucro). Esse percentual é estabelecido em cada caso de acordo com as peculiaridades de cada mercadoria.

BC = (Valor mercadoria + frete + IPI + outras despesas) x Margem de lucro

Margem de valor agregado

A margem de valor agregado será determinada com base em preços usualmente praticados no mercado, obtidos por levantamento, ainda que por amostragem ou através de informações e outros elementos fornecidos por entidades representativas dos setores, adotando-se a média ponderada dos preços coletados. A mercadoria submetida ao regime de substituição tributária em operação interestadual terá a margem de valor agregado estabelecida em Convênio ou Protocolo.

Esse campo o escritório de contabilidade deverá fornecer ao cliente. 

Forma para realizar o cálculo

Devemos pensar em dois pontos básicos: trabalhar com o ICMS da OPERAÇÃO PRÓPRIA e o ICMS DAS OPERAÇÕES SUBSEQUENTES. Assim Temos:

O ICMS da operação própria - realizará uma seqüência de operações matemáticas para chegar a um valor que será utilizado no próximo item.
O ICMS das operações subsequentes - realizará os cálculos necessários para chegar ao valor do ICMS – ST.

Vou exemplificar. Pense em uma operação realizada por um fabricante de brinquedos estabelecido no estado de São Paulo, vendendo à um cliente do mesmo estado. A venda é de R$ 1.000,00 e com IPI calculado a uma alíquota de 5%, teremos:

Nosso 1º passo será realizar o cálculo do ICMS de Operação Própria:
ICMS da operação própria = R$ 1.000,00 x 18% (SP para SP) = R$ 180,00*
* Devemos guardar esse resultado em uma variável para uso futuro

2º Passo, calcular a Base de Cálculo ST:
Base cálculo da ST = R$ 1.000,00 + R$ 50,00 (5% de IPI) + 52% (margem de valor agregado) = R$ 1.596,00

3º Passo, encontrar o valor do ICMS ST
R$ 1.596,00 x 18% (SP para SP) = R$ 287,28

O valor do imposto substituição será a diferença entre o calculado no 1º passo e o do 3º passo. Você se lembra daquela variável guardada no 1º passo? Pois bem, agora usaremos ela para realizar o cálculo da diferença.

R$ 287,28 – R$ 180,00 = R$ 107,28

Pronto, o valor do ICMS – ST é R$ 107,28