Sample VCard reader for vCard versions 2.1, 3.0 and 4.0.
This is under development and worked well with as many samples I could find. By testing with more samples we can improve this.
Code: Select all
/*
*
* vcard.prg
* G.N.Rao
* Apr 30-2013 06:27 PM
*
*/
#include "FiveWin.Ch"
#include "hbcompat.ch"
#include "ord.ch"
#include "xbrowse.ch"
//----------------------------------------------------------------------------//
REQUEST DBFCDX
//----------------------------------------------------------------------------//
function Main()
local cCard, aInfo
local oCard
local cFile
do while ! Empty( cFile := cGetFile( "VCard (*.vcf) |*.vcf|", "Select a VCard File", ;
nil, cFilePath( ExeName() ) ) )
oCard := TVCard():New( cFile )
oCard:Display()
enddo
return (0)
//----------------------------------------------------------------------------//
init procedure PrgInit
SET DATE GERMAN
SET CENTURY ON
SET TIME FORMAT TO "HH:MM:SS"
SET EPOCH TO YEAR(DATE())-50
SET DELETED ON
SET EXCLUSIVE OFF
RDDSETDEFAULT( "DBFCDX" )
XbrNumFormat( 'E', .t. )
SetKinetic( .f. )
SetGetColorFocus()
return
//----------------------------------------------------------------------------//
CLASS TVCard
DATA nVersion AS NUMERIC
DATA lValid AS LOGICAL
//
DATA cFile
DATA cName, cNameFmt, cAdrHome, cAdrWork, cAdrHomeFmt, cAdrWorkFmt, ;
cTitle, cKind, cGender, cOrg, cRole, cNickName, cNote, ;
cPhoneHome, cPhoneWork, cFaxHome, cFaxWork, cPhoneCell, ;
cEmail, cPhotoRef, cPhotoBuf ;
AS CHARACTER
DATA dDOB AS DATE INIT CTOD( '' )
//
//
METHOD New( cFile ) CONSTRUCTOR
METHOD ReadFile( cFile ) INLINE ::ReadCardText( MemoRead( cFile ) )
METHOD ReadCardText( cText )
METHOD Display()
//
METHOD SaveToDBF() VIRTUAL
METHOD ReadFromDBF() VIRTUAL
METHOD SaveAsText() VIRTUAL
METHOD RestoreFromText( cText ) VIRTUAL
METHOD WriteVCard() VIRTUAL
ENDCLASS
//----------------------------------------------------------------------------//
METHOD New( cFile ) CLASS TVCard
if ! Empty( cFile )
if Len( cFile ) < 256 .and. Lower( cFileExt( cFile ) ) == "vcf"
::cFile := cFileNoPath( cFile )
::ReadFile( cFile )
else
::ReadCardText( cFile )
endif
endif
return Self
//----------------------------------------------------------------------------//
METHOD ReadCardText( cText ) CLASS TVCard
local aLines, aLine
local c, cVal, n, nAt
local lAssigned, cAttr
cText := StrBetween( cText, "BEGIN:VCARD", "END:VCARD" )
cText := StrTran( cText, CRLF + ' ', '' )
cText := StrTran( cText, CRLF, Chr(10) )
cText := StrTran( cText, Chr(10) + ' ', '' )
cText := RemAll( cText, 10 )
aLines := HB_ATokens( cText, Chr(10) )
for n := 1 to Len( aLines )
c := aLines[ n ]
aLines[ n ] := { Upper( BeforAtNum( ':', c, 1 ) ), AfterAtNum( ':', c, 1 ), "" }
if aLines[ n, 1 ] = "PHOTO" .and. ;
"ENCODING" $ aLines[ n, 1 ] .and. ;
"BASE64" $ aLines[ n, 1 ]
aLines[ n, 2 ] := DecodeBase64( aLines[ n, 2 ] )
endif
next n
if aLines[ 1, 1 ] = "VERSION"
::nVersion := Val( aLines[ 1, 2 ] )
endif
if ::nVersion == 2.1 .or. ::nVersion == 3 .or. ::nVersion == 4
::lValid := .t.
endif
if ! ::lValid
return Self
endif
ADel( aLines, 1, .t. )
for n := 1 to Len( aLines )
if !( ';' $ aLines[ n, 1 ] )
aLines[ n, 1 ] += ';'
endif
c := aLines[ n, 1 ]
if ( nAt := At( '.', c ) ) > 0 .and. nAt < At( ';', c )
aLines[ n, 3 ] := Trim( BeforAtNum( c, '.', 1 ) ) // Group
aLines[ n, 1 ] := LTrim( AfterAtNum( c, '.', 1 ) )
endif
next
for each aLine in aLines
c := aLine[ 1 ]
cVal := aLine[ 2 ]
lAssigned := .f.
for each cAttr in { "TITLE", "GENDER", "KIND", "ORG", "ROLE", "NICKNAME", "NOTE" }
if c = cAttr + ';'
OSend( Self, "_c" + cAttr, cVal )
lAssigned := .t.
exit
endif
next
if lAssigned
loop
endif
//
do case
case c = 'N;' ; ::cName := cVal
case c = 'FN;' ; ::cNameFmt := cVal
case c = 'ADR;'
if "WORK" $ c
::cAdrWork := cVal
endif
if "HOME" $ c .or. !( "WORK" $ c )
::cAdrHome := cVal
endif
case c = 'LABEL;'
cVal := StrTran( cVal, "=0A=0D", CRLF )
cVal := StrTran( cVal, "=0D=0A", CRLF )
cVal := StrTran( cVal, "=20", " " )
cVal := FW_ValToExp( cVal )
if "WORK" $ c
::cAdrWorkFmt := cVal
endif
if "HOME" $ c .or. !( "WORK" $ c )
::cAdrHomeFmt := cVal
endif
case c = 'TEL;'
if "CELL" $ c
::cPhoneCell := cVal
else
if "WORK" $ c
if "FAX" $ c
::cFaxWork := cVal
else
::cPhoneWork := cVal
endif
endif
if "HOME" $ c .or. !( "WORK" $ c )
if "FAX" $ c
::cFaxHome := cVal
else
::cPhoneHome := cVal
endif
endif
endif
case c = 'EMAIL;'
::cEmail := cVal
case c = 'PHOTO;'
if IsBinaryData( cVal )
::cPhotoBuf := cVal
else
::cPhotoRef := cVal
endif
case c = 'BDAY;'
c := cVal
::dDOB := STOD( Left( StrTran( StrTran( c, '-', '' ), '/', '' ), 8 ) )
endcase
next
return Self
//----------------------------------------------------------------------------//
METHOD Display() CLASS TVCard
local oDlg, oFont, oImage
local nRow, cAdrHomeFmt := '', cAdrWorkFmt := ''
DEFINE FONT oFont NAME "TAHOMA" SIZE 0,-14
DEFINE DIALOG oDlg SIZE 800,500 PIXEL FONT oFont ;
TITLE ::cFile + " (Version : " + LTrim( Str( ::nVersion ) ) + " )"
if ! Empty( ::cAdrHomeFmt )
cAdrHomeFmt := &( ::cAdrHomeFmt )
endif
if ! Empty( ::cAdrWorkFmt )
cAdrWorkFmt := &( ::cAdrWorkFmt )
endif**
nRow := 10
@ nRow,10 SAY "NameFmt : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cNameFmt SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
/*
nRow += 12
@ nRow,10 SAY "Name : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cName SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
*/
nRow += 12
@ nRow,10 SAY "Gender : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cGender SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "Title : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cTitle SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "Role : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cRole SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "Orgnisn : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cOrg SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "DateOfBirth : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::dDOB SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "PHONES (Work) : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cPhoneWork SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "(Home) : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cPhoneHome SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "FAX (Work) : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cFaxWork SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "(Home) : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cFaxHome SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "CellPhone : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cPhoneCell SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "Email : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cEmail SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
/*
nRow += 12
@ nRow,10 SAY "ADDR (Work) : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cAdrWork SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 12
@ nRow,10 SAY "(Home) : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY ::cAdrHome SIZE 200,10 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
*/
nRow += 12
@ nRow,10 SAY "MailWork : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY cAdrWorkFmt SIZE 200,30 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 32
@ nRow,10 SAY "MailHome : " SIZE 50,10 PIXEL OF oDlg RIGHT
@ nRow,61 SAY cAdrHomeFmt SIZE 200,30 PIXEL OF oDlg COLOR CLR_BLACK,CLR_WHITE BORDER
nRow += 30
if ! Empty( ::cPhotoBuf )
@ 10,290 IMAGE oImage SIZE 100,100 PIXEL OF oDlg
WITH OBJECT oImage
:LoadFromMemory( ::cPhotoBuf )
:Refresh()
END
elseif ! Empty( ::cPhotoRef )
@ 10,290 SAY ::cPhotoRef SIZE 100,100 PIXEL OF oDlg COLOR CLR_HRED,CLR_WHITE BORDER
endif
@ nRow-14,350 BUTTON "Close" SIZE 40,14 PIXEL OF oDlg ACTION oDlg:End()
ACTIVATE DIALOG oDlg CENTERED
RELEASE FONT oFont
// QuickDisplay
XBrowse( Self )
return nil
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
static function StrBetween( cStr, cAfter, cBefore, nOccur )
return BeforAtNum( cBefore, AfterAtNum( cAfter, cStr, IfNil( nOccur, 1 ) ), 1 )
//----------------------------------------------------------------------------//
static function GetXmlTag( cText, cTag, nOccur )
local cRet := nil
local c, n1, n2
n1 := At( '<' + cTag + '>', cText )
n2 := At( '<' + cTag + '/>', cText )
if n2 > 0 .and. n2 < n1
cRet := ""
cText := SubStr( cText, n2 + Len( '<' + cTag + '/>' ) + 1 )
elseif n1 > 0
c := SubStr( cText, n1 + Len( '<' + cTag + '>' ) + 1 )
n2 := At( '</' + cTag + '>', c )
if n2 > 0
cRet := Left( c, n2 - 1 )
cText := SubStr( c, n2 + Len( '<' + cTag + '/>' ) + 1 )
endif
endif
return cRet // nil -> tag not found, '' -> empty value
//----------------------------------------------------------------------------//
function DecodeBase64( cIn )
local cOut := ""
do while ! Empty( cIn )
cOut += b64_sub( Left( cIn, 4 ) )
cIn := SubStr( cIn, 5 )
enddo
return cOut
//----------------------------------------------------------------------------//
static function b64_sub( c4 )
static cRef := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
local a,b,c,d
local x,y,z
local cRet
c4 := Left( c4 + "==", 4 )
a := At( SubStr( c4, 1, 1 ), cRef ) - 1
b := At( SubStr( c4, 2, 1 ), cRef ) - 1
c := At( SubStr( c4, 3, 1 ), cRef ) - 1
d := At( SubStr( c4, 4, 1 ), cRef ) - 1
x := a * 4
x += Int( b / 16 )
if c == -1
return Chr( x )
endif
b := b % 16
y := b * 16
y += Int( c / 4 )
if d == -1
return Chr( x ) + Chr( y )
endif
c := c % 4
z := c * 64
z += d
return Chr( x ) + Chr( y ) + Chr( z )
//----------------------------------------------------------------------------//