2007年度CPA全国统一考试上海考区报名简章
2007-04-18
更新时间:2006-02-20 00:00:00作者:未知
一、前言
随着Internet的不断普及,网上的各种素材和资料越来越多,也越来越丰富,这给网络远程教育的迅速发展带来了机会。越来越多的网络课件被开发出来,给老师和学生的教与学带来了极大的方便。可是这些课件一般都是由某个或某些老师根据自己的需要和讲课习惯来编写制作的,有很浓的个人特色。而对于其他的学校、其他的老师,这些课件往往不能满足他们的要求。但是制作自己课件又需要很大的工作量,导致许多老师对于网络远程教育望而止步。怎样才能够让老师们能快速,方便的制作自己的课件呢?
二、备课系统
本文介绍一个由暨南大学软件工具研究所研制开发的网络课件开发工具--老师备课系统。这个系统的主要功能是将文字,图片,声音,动画等素材以编辑文档的方式制作成老师的课件,然后将课件转换成网页的形式发布到网上,老师就可以进行远程教学了。系统的操作界面与Microsoft
word非常相似(如图1),对于那些熟悉word的老师来说操作起来就驾轻就熟了。系统还提供课件框架生成器,以树形目录的方式管理课件中的网页,这样老师讲课就可以更加清晰有条理了。
图1
系统用的核心技术是将Microsoft word支持的一种通用的文件格式--Rich text format(RTF),转换成网页的格式--html。对于许多人来说,html已经十分熟悉了,可是对于RTF则不太了解,下面就来分析一下RTF的结构。
三、RTF文件格式
RTF的结构其实并不复杂,实际上每个RTF文件都是一个文本文件,里面包括"命令"和"正文",然后由编辑器来解释这个文本文件,分析里面的各种命令,并将相应的正文显示出来。
RTF文件中的命令都是一些以""符号开始的字符串,例如在文件的一开始就以"rtf"来表示这个文件是一个RTF格式的文件。另外一个完整的RTF文件包括文件头和文件体两部分。在文件头中包括字体表、文件表、颜色表的几个数据结构,文件体中的字体、表格的风格都是根据文件头中的信息来格式化的。文件头中的每一个表都以一对大括号括起来。下面就是一个颜色表的例子。
{colortbl;red0green0blue0;red0green0blue255;red0green255blue255;red0green255blue0;red255green0blue255;red255green0blue0;red255green255blue0;red255green255blue255;red0green0blue128;red0green128blue128;red0green128blue0;red128green0blue128;red128green0blue0;red128green128blue0;red128green128blue128;red192green192blue192;}
首先由一个"colortbl"来表示大括号内是颜色表,然后由red0green0blue0来表示正文显示时用到的一种颜色。这种颜色的红绿蓝分量分别都是0。其他以此类推,两种颜色之间用分号隔开。在内容中以"cf0"命令来表示当前的文字用第0号颜色来显示。
文件体由版面格式化命令、正文和各种特殊命令组成的。版面格式化命令是用来控制要显示的正文的字体,颜色和其他与版面有关的项目的。而特殊的命令则是在显示一些例如图象,表格等特殊正文时所使用的。有一些特殊命令会用到大括号来括住他的一些子命令,但无论如何,左大括号的数目和右大括号的数目中是一一对应的(注意:整个RTF文件也由一对大括号括起来)。另外,在由于字符""用来表示命令的开始,所以用""来表示正文中的字符""。
对于中文字,RTF文件进行了特殊的处理,众所周知,一个中文字是由一个16位的数字组成的内码来表示的,也就是两个ASCII字符的长度。RTF文件中并不是简单的将这两个ASCII字符放在正文中,因为这样会引起特殊ASCII字符和中文字的冲突。RTF文件使用了命令的方式来表示中文字:"'内码1'内码2"其中的内码是以文本的方式存储的。例如"老师备课系统"在RTF中表示为"'c0'cf'ca'a6'b1'b8'bf'ce'cf'b5'cd'b3"。
另外,RTF文件对图形的处理是一个很重要的问题,也是实现备课系统的一个难点。在RTF文件中,图形以两种方式存在。第一种是直接嵌入的方式,以"pict"命令开始,第二种方式将图片作为OLE对象嵌入,以"object"命令开始。并且无论以那种方式,整个图片的数据都是由一对大括号括起来的(即"{object
……}")。每一个OLE对象在RTF中分为两个部分,第一部分是OLE的数据,如果RTF的文件解释器能进行OLE操作时,可以利用这部分的数据得到OLE的属性参数并对其进行修改显示,第二部分由"result"命令引出,后面跟的其实是一个图片,当RTF的文件解释器不能进行OLE操作时,则可以简单的显示该图片。但这样就不能对该OLE对象进行修改了。图片数据是以Microsoft内部使用的一种矢量图"METAFILE"格式来存储的。在RTF中存放了一个完整的METAFILE文件,但其数据是以ASCII字符的方式存在。由于大多数浏览器都不支持这种格式的图片文件,因此需要将其转换成通用的图片格式,例如:BMP、GIF、JPG等等,但是在Microsoft
Windows的软件开发手册中没有对METAFILE进行说明,因此无法对起进行直接的转换,但是在Microsoft Windows的软件开发包(SDK)(software
develop kid)中有两个函数"GetMetaFile"和"PlayMetaFile",这两个函数可以将一个METAFILE格式的图片直接显示在屏幕上,这样就为格式的转换提供了方便,方法是:首先将以ASCII字符形式存放在RTF中的METAFILE文件变成以16进制数形式存放的独立的文件保存在磁盘的一个临时文件中,然后用"GetMetaFile"和"PlayMetaFile"将其显示在屏幕上(这里的屏幕实际上是一个虚拟的屏幕,即在内存中建立一个假的屏幕数据结构--Memory
DC,里面有虚拟的显示内存,因此在实际的屏幕上看不见图片的显示)。然后用抓图的方法就可以将图片的像素取出来并将其保存成BITMAP图片格式文件了。再通过一些通用的图形转换程序就可以将其保存成容量比较小的GIF或JPG格式了。(具体程序如附录1)
下面具体介绍一下从RTF转换成HTML的流程,流程图如图2:
图2
转换的流程其实也是很简单的,就是搜寻命令字的前导符"",然后对其后面的命令进行相应的处理,要注意的是大括号的匹配,因为这会直接影响到命令的作用域问题,一般在进入一个命令的处理程序后,如果发现有左大括号存在,则这个命令会一直起作用,直到有相应的右括号与其相匹配为止,如果有大括号的嵌套,则命令将一直起作用到所有的左大括号都有相应的右大括号与其匹配为止(正文中的大括号用"{"、"}"来表示)。
有了RTF到HTML的转换器,备课系统可以说已经完成了一半了(图3所示就是图1的RTF文档转成的html后的效果)。
图3
四、课件框架生成器
为了老师上课的方便,单单将一个文档变成网叶是不够的,老师讲课是根据一定的层次、一定的顺序来讲课的,所以还要增加一个课件框架生成器,这里利用了一个控件"Treeview",这个控件可以显示一个树形的数据结构,利用每个节点带有的属性(key表示接点的ID;tag表示节点所连接的网页路径;text表示节点的名称)就可以树的形式显示一个课件的层次关系了。(如图4)
图4
这个树的结构可以存盘,以便下次老师修改。另外还可以通过javascript将其在网页上显示出来,具体的做法是:首先编写一段javascript程序(如附录2)用来把一个数组在网页上显示成树结构形式,其中数组的部分用一个标识符号标记,当每次要生成课件框架的时候先将数形数据结构以数组的形式存放,然后将数组信息替换掉这个标识符号,这样在网页上看到的就是Treeview控件中的数形结构。(图5就是图4的树结构在ie上显示的结果)
图5
为了老师使用的方便,系统还提供了超连接、资源管理器、java小程序连接和声音图象连接等功能。这样就形成了一套足够老师日常备课所使用的备课系统。另外,系统还提供了课件的上载功能,只要老师按要求设置好服务器,以后增加或修改课件是非常方便的。但由于我们的着眼点是面向老师使用的系统而不是给一些专业人士使用的,为了使老师更加方便的使用本系统,系统将一些复杂的操作尽量简化,这样做带来的结果是功能不能与"fontpage"等专业软件相提并论论,可是本系统也有自己的特点是"fontpage"没有的,例如"课件框架生成器"。另外,word里面也有将RTF另存为html的功能,但是起转换器经常将不能将图片完全转换出来。而且老师要用word来制作一个完整的课件需要许多工具的配合,给一些对电脑不太熟悉的老师带来了很大的麻烦。本系统为老师提供了完整的制作课件的环境,从最初的制作到最后的发布到网上,都有支持,因此与其他的一些系统相比,有其独特的地方。当然本系统也还有许多不足之处,例如对表格的转换,由于RTF文档中的命令随着要求的不断提高而改变,特别是表格的处理命令每个版本的RTF都有修改,因此很难对表格进行支持,所以需要老师将表格转换成图片才能显示表格。
附录1 将metafile转换成bitmap的程序段
int WmfToBmp(int width,int height,char *metafile,char *targetBmp)
{
HMETAFILE hmf;
OFSTRUCT of;
int fh;
HDC hMemDC,hDisplayDC;
HBITMAP hbmp,hold;
BITMAPFILEHEADER hdr;
LPBITMAPINFOHEADER lpbmpih;
HGLOBAL hg;
unsigned int j;
char FileBuffer[640*480];
hmf=::GetMetaFile(metafile);
hDisplayDC=CreateDC("DISPLAY",NULL,NULL,NULL);
hMemDC=::CreateCompatibleDC(hDisplayDC);
hbmp=::CreateCompatibleBitmap(hDisplayDC,width,height);
hold=(HBITMAP)::SelectObject(hMemDC,hbmp);
PatBlt(hMemDC,0,0,width,height,WHITENESS);
SetMapMode(hMemDC,MM_ANISOTROPIC);
SetWindowOrgEx(hMemDC,0,0,NULL);
SetViewportExtEx(hMemDC,width,height,NULL);
PlayMetaFile(hMemDC,hmf);
hbmp=(HBITMAP)::SelectObject(hMemDC,hold);
if((fh=OpenFile(targetBmp,&of,OF_CREATE|OF_READWRITE))==-1)
{
return 0;
}
hdr.bfType=0x4d42;
hdr.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+2*256*sizeof(RGBQUAD)+width*height;
hdr.bfReserved1=0;
hdr.bfReserved2=0;
hdr.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);
_lwrite(fh,(LPSTR)&hdr,sizeof(BITMAPFILEHEADER));
hg=GlobalAlloc(GHND,sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD));
lpbmpih=(LPBITMAPINFOHEADER)GlobalLock(hg);
lpbmpih->biSize=sizeof(BITMAPINFOHEADER);
lpbmpih->biWidth=width;
lpbmpih->biHeight=height;
lpbmpih->biPlanes=1;
lpbmpih->biBitCount=8;
lpbmpih->biCompression=0;
lpbmpih->biSizeImage=width*height;
lpbmpih->biXPelsPerMeter=3790;
lpbmpih->biYPelsPerMeter=3780;
lpbmpih->biClrUsed=256;
lpbmpih->biClrImportant=0;
j=GetDIBits(hMemDC,hbmp,0,height,NULL,(BITMAPINFO*)lpbmpih,DIB_RGB_COLORS);
lpbmpih->biSize=sizeof(BITMAPINFOHEADER);
_lwrite(fh,(LPSTR)lpbmpih,sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD));
j=GetDIBits(hMemDC,hbmp,0,height,(LPSTR)FileBuffer,(BITMAPINFO*)lpbmpih,DIB_RGB_COLORS);
if(j==0)
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL );
MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
LocalFree( lpMsgBuf );
}
_hwrite(fh,(LPSTR)FileBuffer,256*sizeof(RGBQUAD)+width*height);
_lclose(fh);
GlobalUnlock(hg);
GlobalFree(hg);
DeleteDC(hMemDC);
DeleteDC(hDisplayDC);
DeleteObject(hbmp);
DeleteObject(hold);
DeleteMetaFile(hmf);
return 0;
}
附录2 将数组显示在网页上的javascript程序
<html>
<head>
<style type="text/css">
<!--
.font {font-size: 9pt ; line-height:13pt;}
.fontlittle{font-size:9pt;}
A{text-transform: none; text-decoration: none;color:blue;}
a:hover {text-decoration:underline;color:red;}
-->
</style>
<title> 老师备课系统 </title>
<SCRIPT LANGUAGE="javascript">
<!-- begin hiding of script
var ItemName=new Array();
var ItemLink=new Array();
var ItemID=new Array();
var ItemPre=new Array();
var IDArray=new Array();
var IDLen;
var ItemNumber=@%TOTALITEMNUMBER%@;
var TreeFileName="@%TREEFILENAME%@";
function Initialize()
{
@%INITIALIZEITEM%@
if(location.search==""){IDLen=0;}
else
{
var tmpstr=new String();
tmpstr=location.search;
tmpstr=tmpstr.substring(1,tmpstr.length);
IDLen=tmpstr.length;
for(i=0;i<tmpstr.length;i++)
{
IDArray[i]=tmpstr.substring(i,i+1);
}
}
}
function Extend(layer)
{
var FatherID="";
var ChildID=0;
for(j=0;j<layer-1;j++)
{
FatherID+=IDArray[j];
}
ChildID=FatherID;
ChildID+=IDArray[layer-1];
for(i=0;i<ItemNumber;i++)
{
if(ItemID[i].length==layer)
{
if(ItemID[i].substring(0,ItemID[i].length-1)==FatherID)
{
if(ChildID==ItemID[i])
{
for(k=0;k<layer;k++)
{ document.write(" "); }
document.write("<a href='"+TreeFileName+"?"+ItemID[i].substring(0,ItemID[i].length-1)+"'><img
src='img/sub.gif' border=0></a><a href='"+ItemLink[i]+"' target=main>"+ItemName[i]+"</a><br>");
l=i;
Extend(layer+1);
i=l;
}
else
{
if(ItemPre[i]!="0")
{
for(k=0;k<layer;k++)
{ document.write(" "); }
document.write("<a href='"+TreeFileName+"?"+ItemID[i]+"'><img src='img/plus.gif'
border=0></a><a href='"+ItemLink[i]+"' target=main>"+ItemName[i]+"</a><br>");
}
else
{
for(k=0;k<layer;k++)
{ document.write(" "); }
document.write("<img src='img/point.gif' border=0><a href='"+ItemLink[i]+"'
target=main>"+ItemName[i]+"</a><br>");
}
}
}
}
}
}
// end script hiding -->
</SCRIPT>
</head>
<body>
<font size="2">
<SCRIPT LANGUAGE="javascript">
<!-- begin hiding of script
Initialize();
for(i=0;i<ItemNumber;i++)
{
if(ItemID[i].length==1)
{
if(IDArray[0]==ItemID[i])
{
document.write("<a href='"+TreeFileName+"?"+ItemID[i].substring(0,ItemID[i].length-1)+"'><img
src='img/sub.gif' border=0></a><a href='"+ItemLink[i]+"' target=main>"+ItemName[i]+"</a><br>");
l=i;
Extend(2);
i=l;
}
else
{
if(ItemPre[i]!=0)
{
document.write("<a href='"+TreeFileName+"?"+ItemID[i]+"'><img src='img/plus.gif'
border=0></a><a href='"+ItemLink[i]+"' target=main>"+ItemName[i]+"</a><br>");
}
else
{
document.write("<img src='img/point.gif' border=0><a href='"+ItemLink[i]+"'
target=main>"+ItemName[i]+"</a><br>");
}
}
}
}
// end script hiding -->
</SCRIPT>
</font>
</body>
</html>