c# 基于wpf,开发OFD电子文档阅读器
前言
OFD是国家标准版式文档格式,于2016年生效。OFD文档国家标准参见《电子文件存储与交换格式版式文档》。既然是国家标准,OFD随后肯定会首先在政务系统使用,并逐步推向社会各个方面。OFD是在研究当下各类文件格式后,推出的标准,有如下优点:
1产权属于自主产权
2具有便携性:文件小,可压缩比率大。测试显示生成的文件体量比PDF还要小。
3具有开放性:易于入门,对于使用者来说更具开放性。
4具有扩展性:预留了可扩展入口和自定义标引,设置了非接触式引用机制,为特性化提供支持。
5呈现效果与设备无关,在各种设备上阅读、打印或印刷时,版面固定、不跑版。
6应用广泛:无论是电子商务、电子公务,还是信息发布、文件交换,档案管理等都需要版式文档的技术支持。
关于标准,我也要吐槽一下。OFD标准是国内几家专业的电子文档处理公司参与起草的;标准文档(注:以下用”标准”特指OFD标准)只有126页,在我看来,标准对技术细节的描述过于简单,没有一定的技术背景很难看懂。与此形成鲜明对比的是pdf标准,有1000多页。我在网上也没找到文字版的标准,特别不利于阅读和参考。
ofd阅读器程序(已集成了转图、转PDF功能)下载。
我最近一直研究ofd标准,试图写一款阅读器,已初有成果。具有ofd转换为pdf、转为图片等特色功能。界面如下:
本文就把我开发的过程做简单介绍。
OFD标准简介
简而言之,OFD存储是采用压缩技术,描述采用XML格式。这一点与微软的word文档(docx)格式很类似。标准可能参考了微软的处理方式;在技术上也要实事求是,国标这种格式不是独创和领先的。将OFD格式文件解压后,会看到如下目录和文件:
文件中会包括资源文件(图片、字体库等)。XML会对资源存放,图元(文字、图像等)显示做描述,阅读软件会根据这些描述呈现出一致的显示效果。
开发OFD阅读软件步骤
国内流行的ofd阅读软件应该是福昕和数科开发的,这两款我都用过。我还要吐槽一下:
1)福昕阅读器帮助文档是ofd格式,但是无法用数科的阅读器打开。
2)有些ofd文档中xml标记,在标准中找不到,是某些公司独创的?
这些软件都是用C++开发的,用到了QT。同样情况下,相比于C#,C++开发软件难度肯定会大增。在windows平台开发界面,WPF应该是最好的库了。WPF虽然出现十几年了,大家好像对此还很陌生。主要现在是BS的天下;不是WPF不够好,是生不逢时。
1对OFD文件解压缩
OFD文件其实就是压缩文件,解压后的文件也有目录结构。该模块的功能是获取每个文件的路径和数据。
usingSystem;
usingSystem.Collections.Generic;
usingSystem.IO;
usingSystem.IO.Compression;
usingSystem.Linq;
namespaceWpfOfdReader.OfdFileType
{
classOfdFileReader
{
ZipArchive_zipArchive;
publicvoidReadZipFile(stringfileName)
{
_zipArchive=ZipFile.OpenRead(fileName);
}
publicvoidClose()
{
if(_zipArchive!=null)
_zipArchive.Dispose();
}
publicListAllFileItem
{
get
{
return_zipArchive.Entries.Select(o=>newOfdFileItemInfo(o)).ToList();
}
}
privateZipArchiveEntryGetArchiveEntry(ZipFilePathpath)
{
foreach(ZipArchiveEntryentryin_zipArchive.Entries)
{
if(entry.FullName==path.FulleName)
{
returnentry;
}
}
returnnull;
}
publicstaticbyte[]GetFileBuffer(ZipArchiveEntryentry)
{
ListlistBuffer=newList();
using(Streams=entry.Open())
{
while(true)
{
byte[]buffer=newbyte[10];
intn=s.Read(buffer,0,buffer.Length);
if(n<=0)
break;
if(n==buffer.Length)
{
listBuffer.Add(buffer);
}
else
{
Array.Resize(refbuffer,n);
listBuffer.Add(buffer);
break;
}
}
}
inttotalLen=0;
listBuffer.ForEach(o=>totalLen+=o.Length);
byte[]result=newbyte[totalLen];
intindex=0;
foreach(byte[]bufferinlistBuffer)
{
Buffer.BlockCopy(buffer,0,result,index,buffer.Length);
index+=buffer.Length;
}
returnresult;
}
}
}
2找到需要展示的page
顺着路线OFD.xml--> Document.xml-->Pages,找到最终需要展示的page页。Page页包含三类节点:PathObject、ImageObject,暨对应标准中的三类图元。需要对这三类节点建模。这三个类共同继承父类PageObject。所有的图元都有绘制区域、坐标变换、裁剪等共性,所有这些由PageObject类处理。
publicclassPageObject
{
publicstringID{get;set;}
publicPageLayerParentLayer{get;set;}
publicstringPageFileLoc=>ParentLayer.ParentPage.PageFileLoc;
XmlNode_xmlNode;
publicstringBoundary{get;set;}
publicstringCTM{get;set;}
publicOfdClipsGroupClipsGroup{get;set;}
publicvoidSetPageObject(PageLayerlayer,XmlNodexmlNode)
{
_xmlNode=xmlNode;
ID=XmlHelper.GetXmlAttributeValue(xmlNode,"ID");
ParentLayer=layer;
Boundary=XmlHelper.GetXmlAttributeValue(xmlNode,"Boundary");
CTM=XmlHelper.GetXmlAttributeValue(xmlNode,"CTM");
foreach(XmlNodechildNodeinxmlNode.ChildNodes)
{
if(childNode.Name==OfdClipsGroup.XML_Name)
{
ClipsGroup=OfdClipsGroup.FromXml(childNode);
break;
}
}
}
publicstringGetAttributeValue(stringname)
{
stringresult=XmlHelper.GetXmlAttributeValue(_xmlNode,name);
returnresult;
}
}
3创建WPF显示模型
图像精确定位需要用到Canvas控件作为容器。绘制大量图形需要用到轻量级绘制模型DrawingVisual。在此基础上,派生了绘制基础模型OfdVisual,此模型对应PageObject。
publicclassOfdVisual:DrawingVisual
{
publicOfdVisual()
{
}
protectedDrawingCanvas_drawingCanvas;
publicDrawingCanvasDrawingCanvas
{
get
{
return_drawingCanvas;
}
}
publicboolIsAddToCanvas
{
get
{
return_drawingCanvas!=null;
}
}
internalvoidAddToCanvas(DrawingCanvasdrawingCanvas)
{
if(_drawingCanvas==drawingCanvas)
return;
_drawingCanvas=drawingCanvas;
_drawingCanvas.AddVisual(this);
}
publicvoidReomveFromCanvas()
{
if(_drawingCanvas!=null)
{
_drawingCanvas.DeleteVisual(this);
}
}
publicvirtualvoidShow(boolvisiable,booleven=false)
{
}
publicPointBoundaryLocation{get;set;}
publicSizeBoundarySize{get;set;}
publicMatrixTransformObjectTransform{get;protectedset;}
publicVisualClipsGroupObjectClipsGroup{get;protectedset;}
publicvoidSetPageObject(PageObjectpageObject)
{
OfdHelper.ParseBoundary(pageObject.Boundary,outPointlocation,outSizesize);
BoundaryLocation=location;
BoundarySize=size;
if(!string.IsNullOrEmpty(pageObject.CTM))
{
ObjectTransform=OfdHelper.OfdTextToTransform(pageObject.CTM);
}
if(pageObject.ClipsGroup!=null)
{
ObjectClipsGroup=newVisualClipsGroup(){ClipsGroup=pageObject.ClipsGroup};
}
}
protectedRectClipRect
{
get
{
returnnewRect(0,0,BoundarySize.Width,BoundarySize.Height);
}
}
protectedRectangleGeometryClipGeometry
{
get
{
RectangleGeometrygeometry=newRectangleGeometry(ClipRect);
returngeometry;
}
}
protectedvoidPutBoundary(DrawingContextdc)
{
TranslateTransformtranslateBoundary=newTranslateTransform(BoundaryLocation.X,BoundaryLocation.Y);
dc.PushTransform(translateBoundary);
dc.PushClip(ClipGeometry);
}
protectedvoidPopBoundary(DrawingContextdc)
{
dc.Pop();
dc.Pop();
}
protectedvoidPutTransform(DrawingContextdc)
{
if(ObjectTransform!=null)
{
dc.PushTransform(ObjectTransform);
}
}
protectedvoidPopTransform(DrawingContextdc)
{
if(ObjectTransform!=null)
{
dc.Pop();
}
}
}
有三种类型绘制对象OfdVisualText、OfdVisualPath、OfdVisualImage,派生自OfdVisual。分别处理三种图元数据。所有的绘制操作在函数
publicoverridevoidShow(boolvisiable,booleven=false);
对应文本,绘制函数如下:
voidDrawText()
{
using(DrawingContextdc=RenderOpen())
{
if(ObjectClipsGroup==null)
{
PutBoundary(dc);
PutTransform(dc);
DrawTextInner(dc);
PopTransform(dc);
PopBoundary(dc);
}
else
{
foreach(VisulClipvisulClipinObjectClipsGroup)
{
PutBoundary(dc);
visulClip.PutClip(dc);
PutTransform(dc);
DrawTextInner(dc);
PopTransform(dc);
visulClip.PopClip(dc);
PopBoundary(dc);
}
}
}
}
privatevoidDrawTextInner(DrawingContextdc)
{
inti=-1;
doubledeltaXTotal=0;
doubledeltaYTotal=0;
Pointpt=newPoint();
foreach(FormattedTextformattedTextinFormattedTextCollection)
{
i++;
if(i!=0)
{
if(DeltaCollectionX!=null)
{
doubledeltaX=DeltaCollectionX.GetValue(i-1);
deltaXTotal+=deltaX;
}
if(DeltaCollectionY!=null)
{
doubledeltaY=DeltaCollectionY.GetValue(i-1);
deltaYTotal+=deltaY;
}
}
pt.X=TextLocation.X+deltaXTotal;
pt.Y=TextLocation.Y+deltaYTotal-FormattedTextCollection.FontBaseLine;
dc.DrawText(formattedText,pt);
}
}
绘制前,需要对当前坐标做变换、旋转、剪切等操作。
最近又对程序完善了,增加缩略图和公文索引:
后记
编写阅读器类软件的关键是建模。首先读懂标准,对标准中描述的图元做归类分析,并建立起相应的显示模型。本人做WPF开发很多年了,感觉用WPF开发这类软件并不是非常的难。相比于QT,采用wpf开发有很多优势。如果要完整实现OFD标准,还需要大量的开发,我会逐步完善该软件的功能。
特别说明
ofd阅读器开发语言为c#,具有完全自主产权,没有使用第三方ofd开发包。可以根据你的需求快速定制开发。本阅读器还在开发完善阶段,如有任何问题,可以联系我QQ:13712486。
以上就是c#基于wpf,开发OFD电子文档阅读器的详细内容,更多关于c#wpf开发的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。