Using XML to implement universal web report printing

xiaoxiao2021-03-06  26

The most headache of the application of the B / S structure can be that the report prints, since the browser can only be interacting as the user interface, so you cannot accurately control the client's printer. And many B / S structures often need to complete a very complex report print job. The page printing function that comes with IE can generally not meet the needs. Crystal Report is a commonly used and recommended solution, but if we only need some small-scale report printing, Crystal Report is huge, and customizable is not very good, its print In fact, the print function of IE is also used, and the printing effect cannot be accurately controlled, and you need to register it. So we discussed another way, simply, if you have any of the following needs, you can try this solution. Solution applicability 1. Remote data printing. The data that needs to be printed is not locally, and remote read must be performed. 2. Precisely control the printing effect, including page format, pagination, additional entry, table, and more. 3. For security considerations, you cannot connect directly to the database. The principle of the program is simple. It is very simple. Through XML powerful custom features, we can easily customize all our needs of our format control tags. After dynamic encoding in the server, pass the web server to the client, then on the client Format parsing, directly control the printer directly from the client to print out the reports we need from the client depending on the printed format defined by the server. Technical Selection Since the report print is more complicated, in order to accurately control the print format, the report print job can not be used in the way web browser page printing, and only the print job of the self-programming control client can be taken. Since the .NET Framework's WinForm can be embedded directly into the web page, we have this technology here, but please note that I don't mean that .NET WinForm is the only choice, in fact, you can use any client instead of it, for example Java Applet or ActiveX, even a normal application can be row. Do not allow direct connection to the database, so you can only use the XML file to perform the intermediate data exchange format, and data transmission is performed by the default 80 port of the ordinary web server. In fact, I can't find other ideal programs. Of course, Web Service may be a kind, but it uses SOAP transmission data. From the principle, the XML should be used as the same type technology. Address, I want to use the controlled components written by .NET, the advantage is that it does not need to be registered by the client. A big advantage is relative to ActiveX. 2. Highly higher security than ActiveX. Running below .NET Common Language Runtime 3. Easy to write. I like C # and Visual Studio .NET. 4. There is a very powerful print control. Use the .NET Framework class library. 5. Supports XML technology directly. 6. He is high in IE compatibility. The same is Microsoft products. In addition, it is important to note that the default security level in .NET Framework SP1 and SP2 cannot run the controlled component directly, but in the .NET Framework 1.1 beta, it can be running directly. Server-side You can use an existing server system and database, which does not require newly adding any new hardware devices and new .NET server managers, they are often important guys who take high salary. :) The server's workflow is: 1. Accept the client's standard XML template query. 2. The database data format is required to convert the database data format into a standard XML data format according to the query requirements. 3. Send XML data through the 80 port.

Feasibility Analysis Since most of the database supports data query and conversion in XML format, such as SQL Server 2000, Oracle 9i, IBM DB2, etc. Large-type relationships. Just use simple settings to perform XML data conversion work. If the database cannot support direct XML data conversion, you can use some server-side scripts to convert work, such as JSP, ASP, PHP, and more. The client does not require any special settings, just install a .NET Framework distribution package with a size of 21m, and then open the web page to work. There is also no operating system restriction, it can support it from Windows 98 to Windows XP. Substancy and safety scalability Since the XML standard data format is used as intermediate data exchange, this solution has very good scalability, for example, the client's .NET control can be written in Java Applet, ActivX or VB, VC, etc. The client application is replaced directly. The server can also be arbitrarily selected by IIS or APACHE and other web servers. A database can also be used in any database. Includes SQL Server, Oracle or Access, etc.. This above has already talked, because the length of the article does not make the T-shirt that is given to me, and then emphasizes it just to deepen the readers' cross-platform understanding of XML. :) Safety Since the use of a normal web server transmitting data, it can directly adopt SSL security sockets and other already mature web encryption techniques. At the same time, XML can be encrypted on the data algorithm, and decrypt the client to ensure the security of the transmission. Since the 80-port is used, it is not necessary to add another new dedicated port, which reduces the possibility of security vulnerabilities, and it can easily pass the network firewall and other protective equipment for both parties. The plan design is defined in order to control the format of printing, we define the following format tags, where to refer to HTML naming approach, so it is basically familiar with HTML to see the specific meaning of the label. If you think that these tags have not strong enough, you can also define some more and more precise format tags. Label application example:

true A4 210 297 0 0 0 0 latest transaction contract information Tabulation time: "Y =" 100 "fontname =" "" Y = "100" FontName = " Song "FontSize =" 12 "fontcolor =" black "b =" true "i =" false "u =" true "> unit: element

contract number Product Name < Td width = "50" align = "center" fontname = "Song" fontsize = "12" fontcolor = "black" b = "true" i = "false" u = "false" bgcolor = "white"> transaction volume < / TD> Transaction price Transaction amount Harmoned amount starting price seller Buyer
20021010015 CNR 93 < Td width = "70" align = "left" fontname = "Song" fontsize = "12" fontcolor = "black" b = "true" i = "false" u = "false" BGColor = "White"> 6680 621240 93 6680 Hubei Province State New Star Tractor Factory Zhonghua International Trade Co., Ltd. .........

Note: a) If the server script dynamically generates an XML document, the sending content type should be set to Text / XML (normal HTML page is TEXT / HTML), and the character encoding should be UTF-8, otherwise the encoding error problem will occur. B) The file should be generated in strict accordance with the format specified in XML, otherwise the XML parser will not be parsed. 2. The client can use any application to read the XML file generated by the server side. If you use a desktop application software development tool for VB, Delphi, you can use the MSXML COM parser. It is recommended to use .NET, the interior has integrated XML parser, which can be used directly by using the .NET class library. You can make both desktop applications, via remote calls; can also be embedded in the IE browser, running directly on the web page. Effects Example Diagram Print Preview Preview: 1. If you use .NET, the client must install .NET Framework1.0 running environment, download address is: http://download/.netframesdk/redist/1.0 /W98NT42kmexp/en-us/dotnetredist.exe 2. If you are embedded in the web page, then this program needs to be compiled into a control form (a file extension DLL), then insert the following tag in the web:

For most reports, I only define two format tags: Text and Table, their specific attribute definitions, and other settings of label definition, please refer to the forebelt, here again Figure helps readers understand. As follows:

Structural design:

To describe all style markers, I first define a abstract base class Printelement, which has a virtual method DRAW, then corresponds to the table and text, derived two subclasses from Printelement, which is Table and Text, I also created a Parser The class is used to parse different styles tags and create a corresponding object, which has a static method CreateElement that creates a corresponding object based on different format tags. The structure is shown below: readers who have read "design patterns" must have seen it, this design applies a very famous model in design mode: Abstract Factory. The advantage of using this mode is that the label objects and parsers are independent, reducing the system coupling degree, which is advantageous to increase other format labels in the future (hereinafter will give an instance) and convenient Replace different user interfaces (the client represents a Windows application or a web plugin).

Code:

First, create a new project of "Windows Control Library", write Remoteprint at the project name, as shown below:

Then put the default UserControl1 class in the new project, and its constructor name and file name are changed to PrintControl. Set it back to white, add three press, and set their enable properties to false, the Anchor property is set to Bottom, Right, add a Label control to display program status, its Anchor property Set to Left. As shown below:

Then drag in three print objects from the control bar: PrintDocument, PagesetupDialog, PrintPreviewDialog, as shown below:

All Document properties of PageSetupDialog1 and PrintPreviewDialog1 are set to PrintDocument1.

Then add a new class of Printelement to the project, the code is as follows:

Using system; using system.drawing; namespace remotEprint {public copublic class printelement {public printelement () {} public virtual bool Draw (graphics g) {Return False;}}}

There is only one virtual method DRAW in this class, pay attention to it specifies that a BOOL value needs to be returned. This value is used to indicate whether the label is printed in the page.

Then add a Table new class, the code is as follows:

using System; using System.Xml; using System.Drawing; namespace RemotePrint {public class Table: PrintElement {private XmlNode table; public static int count = 0, pc = 1; public Table (XmlNode Table) {table = Table;} public Override Bool Draw (File: // Table Coordinate INT TABLEX = INT.PARS (Table.Attributes ["X"]. InnerText); int Tabley = Int.Parse (Table.attributes ["Y"]. InnerText ); int x = Tablex, y = Tabley; DrawTopline (g, table); // Painting Table Pen Pen = new pen (color.attributes ["bordercolor"]. innerText), float.parse Table.attributes ["Border"]. InnerText); int trueight = 0; file: // Header Foreach (XMLNode Tr in Table ["TableHead"]. ChildNodes) {Trhem = Int.Parse (Tr.attributes [" Height "]. innerText); DrawTR (x, y, tr, pen, g); y = trheight;} file: // entry for (INT i = 0; i

Bordercolor "]. innerText), float.parse (Table.attributes [" Border "]. InnerText); int width = 0; Foreach (XMLNode Td in table.firstchild.FirstChild) {width = int.Parse (TD. Attributes ["width"]. InnerText);} int x = int.parse (Table.Attributes ["x"]. InnerText); int y = int.parse (Table.Attributes ["y"]. InnerText); g .Drawline (Pen, X, Y, X Width, Y);} File: // Picture of Private Void Drawtr (INT X, INT Y, XMLNode Tr, Pen Pen, Graphics G) {INT Height = Int.Parse (Tr.attributes ["Height"]. InnerText); int width; g.drawline (Pen, X, Y, X, Y height); // Draw left end Foreach (XMLNode TD IN TD) {width = int. PARSE (TD.Attributes ["Width"]. InnerText); DrawTD (X, Y, Width, Height, TD, G); G. Drawline (Pen, X Width, Y, X Width, Y Height); // Right Line G. Drawline (Pen, X, Y Height, X Width, Y Height); // Bottom Line X = Width;}} File: // Sex PRIVATE VOID DRAWTD (int x, int Y, int Width, INT Height, XMLNode TD, Graphics G) {Brush Brush = New Solidbrush (Color.FromName (Td.attributes ["BGColor"]. Innertext); G.FillRectangle (Brush, x, y, width, Height; fontstyle style = FontStyle.Regular; File: // Set font pattern IF (Td.attributes ["b"]. InnerText == "true") style | = fontstyle.bold; if (td.attributes ["i"]. InnerText == "true") style | = fontStyle.italic; if (td.attributes ["u"]. InnerText == "true") style | = fontstyle.underLine; font font = new font (td.attributes ["fontname"] .InnerText, float.Parse (. td.Attributes [ "fontsize"] InnerText), style); brush = new SolidBrush (Color.FromName (td.Attributes [ "fontcolor"] InnerText.)); StringFormat sf = new StringFormat ( );

File: // Set alignment Switch (Td.attributes ["align"]. innerText) {copy "center": sf.cent.Alignment = stringalignment.center; Break; Case "Right": sf.Alignment = stringalignment.near; Break Default: sf.alignment = stringalignment.far; break;} sf.LineAlignment = stringalignment.center; Rectanglef Rect = New Rectanglef (FLOAT) X, (Float) Y, (float) width, (float); g .Drawstring (TD.INNERTEXT, FONT, BRUSH, RECT, SF);}}} The Table class will be independent, all of which are independently inside the table tag, all of which are completed inside the class, so that we are parsing the top layer tag as long as It is directly handed over to the Table class directly to the Table class. Don't care about it.

Add another Text class, the code is as follows:

using System; using System.Xml; using System.Drawing; namespace RemotePrint {public class Text: PrintElement {private XmlNode text = null; public Text (XmlNode Text) {text = Text;} public override bool Draw (Graphics g) {Font font = new font (text.Attributes [ "fontname"] InnerText, int.Parse (text.Attributes [ "fontsize"] InnerText)..); Brush brush = new SolidBrush (Color.FromName (text.Attributes [ "fontcolor" ] .Innertext); g.drawstring (Text.innertext, font, brush, float.parse (text.attributes ["x"]. InnerText), float.parse (text.attributes ["y"]. InnerText)) Return false;}}}

Like the Table class, the Text class completes parsing and printing of the Text tag, but because of the simplicity of Text, it has a lot less code. Both them inherit from Printelement, which is heavy as the implementation of the DRAW method. After that, we also need a parser to parse the top layer label and generate the corresponding object, and its role in this mode is a "factory class", which is responsible for producing the "product" required by the user. code show as below:

using System; using System.Xml; namespace RemotePrint {public class Parser {public Parser () {} public static PrintElement CreateElement (XmlNode element) {PrintElement printElement = null; switch (element.Name) {case "text": printElement = new TEXT (ELEMENT); Break; Case "Table": Printelement = New Table (Element); Break; default: printelement = new printelement (); Break;} Return Printelement;}}} Good, core resolution and label specific The print method has been completed, and now we return to PrintControl to write some code to test our results.

First, you need to reference two namespaces to be used:

Using system.xml; using system.drawing.printing;

Then, before printing, you need to set the pages of the printer according to the PageSetting tag in the XML file, so we write a method to set the printer. Add a private method in the PrintControl class:

private void SettingPrinter (XmlNode ps) {file: // orientation (portrait / landscape) this.printDocument1.DefaultPageSettings.Landscape = bool.Parse (ps [ "landscape"] .InnerText); file: // set the paper type string papername = ps [ "paperkind"] InnerText; bool fitpaper = false; file:. // Get all paper supported by the printer type foreach (PaperSize size in this.printDocument1.PrinterSettings.PaperSizes) {if (papername == size.PaperName) / / See if the printer has the paper type we need {this.printDocument1.defaultpagesettings.papersize = size; fitpaper = true;}}} If there is no need to type, customizable Size this.PrintDocument1.defaultpagesettings.papersize = new papersize ("Custom", INT.PARS (PS ["Paperwidth"]. InnerText), int.Parse (PS ["paperHeight"]. InnerText);}}

Next, we add an XMLDocument object and a static variable calculation page number:

Private xmldocument doc = new xmldocument (); public static int pages = 1;

Then load XML report data for the object in the LOAD event of the control, the code is as follows:

Private Void PrintControl_load (Object Sender, System.EventArgs E) {Try {File: // Load Report XML Data THISLABEL1.TEXT = "Loading report data, please wait a little ..."; doc.load ("http: //localhost/report.xml" ?;this.label1.text = "Report data is loaded!"; this.button1.enabled = this.button2.enabled = this.button3.enabled = true;} catch (Exception EX) {this.label1.text = "An error:" ex.Message;}} Please note that we only put a local test data file, in fact, it can be changed to any place or dynamic from anywhere on the network. XML file, such as the above Doc.Load ("http: //localhost/report.xml") can be rewritten:

Doc.Load ("http://www.nywhere.com/report.xml");

Doc.Load ("http://www.anyywhere.com/report.asp");

Doc.Load ("http://www.anyywhere.com/report.jsp?date=xxx");

Wait, as long as the loaded data is possible to meet the XML data documentation we specified.

Then add a printed event to the control of the control:

Public printControl () {initializationComponent (); this.printDocument1.printpage = new printpageeventhandler (this.pd_printpage);}

The code of the entrustment method is as follows:

private void pd_PrintPage (object sender, PrintPageEventArgs ev) {Graphics g = ev.Graphics; bool HasMorePages = false; PrintElement printElement = null; foreach (. XmlNode node in doc [ "root"] [ "reporttable"] ChildNodes) {printElement = Parser.createElement (Node); // Call the parser Generate the corresponding object try {hasmorepages = Printelement.draw (g); // Need to paid} catches (Exception ex) {this.label1.text = ex. measureage;} } file: // Output page number font font = new font ("black body", 12.0f); brush brush = new solidbrush (color.black); g.drawstring ("No. Pages.Tostring () "Page", Font, Brush, Ev.Marginbounds.Width / 2 ev. Marginbounds.left - 30, ev.pagebounds.Height - 60); if (HasmorePages) {Pages ;} ev.hasmorepages = hasmarepages;}

Three button Click event codes are as follows: @ Page Setup private void button1_Click (object sender, System.EventArgs e) {this.pageSetupDialog1.ShowDialog (); this.printDocument1.DefaultPageSettings = this.pageSetupDialog1.PageSettings;} file : // Print preview private void button2_Click (object sender, System.EventArgs e) {try {this.printPreviewDialog1.ShowDialog ();} catch (Exception ex) {this.label1.Text = ex.Message;}} file: / / Print Private Void Button3_Click (Object sender, system.eventargs e) {TRY {this.printDocument1.print ();} catch (exception ex) {this.label1.text = ex. measure;}}

Ok, our print control is all done here, choose to generate a release version, then copy the generated printControl.dll file to the IIS in the project directory, then create a RemotePrint.htm HTML Format file, plus: , in order to more image And beautiful, you can also make the data you need to print into a web page. If you need to get the XML is a dynamic data source, you can use the dynamic script of ASP to generate the web page, if you need to get the XML is a static Text, you can use XSLT to convert an XML file into a web form directly.

Open the browser, type: http://localhost/remoteprint.htm, if you have already made an XML report data file like me, you can see the effect shown below.

Please note: All the data in the illustration sample is the author's casual virtual. The table data and print data in the web page are not from the same data source, nor deliberately goes right, just to demonstrate the effect, so the webpage shows the report and print There are some new reports in the preview. In practical applications, web display data can be identical to printout data. Program expansion:

So how do you print some special forms of charts, which already mentioned above, which uses this solution that can be very convenient to define the labels you need, in theory, the special chart of any style can be printed. Therefore, this article intends to detail the specific process of adding your own defined label expansion printing format.

First assume that our customers are basically satisfied after watching the printed effect, but there is still a little short, if you need to print some charts? For example, a line diagram, a K-line diagram, a pie chart, a histogram, and the like. With our existing tags, we must first expand our label library to make it more expressive. Here, I will only plan to let our print control learn to draw simple line charts, I hope the reader can give an anti-three to create other various printing effects.

The most basic line diagram is composed of a line connected by the X coordinate axis, Y coordinate axis, and a series of points, therefore, I define the following tags: 1. Linechart: Like Table, TEXT tag, for pattern root label .

Property: None

2. Coordinate: coordinates.

Property: None

3. XCoordinate: X-axis coordinate line

Attributes:

# x: starting point X coordinate value

# y: The starting point Y coordinate value

# Length: Length value

# stroke: thick

# color: Color

# arrow: Is there an arrow?

4. Ycoordinate: Y-axis coordinate line

Attribute: with XCoordinate.

5. Scale: Turning

Tag content: text displayed on the scale

Attributes:

# Length: Distance Point Length Value

# hT: Tariff Highness

# width: Turning Width

# color: Color

# fontsize: Font size

6. Chart: chart root

Property: None

7. Lines: line segment

Attribute value:

# stroke: thick

# color: Color

8. Point: Point

Attribute value:

# x: x coordinate value

# y: y coordinate value

# RADIUS: radius

# color: Color

The structure is shown below:

Below is an XML line diagram example of the label just defined:

100 200 300 400 500 600 700 100 <

/ scale> 200 300

Radius = "5" color = "black" /> completed the label definition, The next step is to modify our program, let him "read" these labels.

First, we will add a new class of LineChart to the project, just like the Table, Text class, which is also inherited from the Printelement class, which also overloads the Draw virtual method. code show as below:

using System; using System.Xml; using System.Drawing; using System.Drawing.Drawing2D; namespace RemotePrint {public class LineChart: PrintElement {private XmlNode chart; public LineChart (XmlNode Chart) {chart = Chart;} public override bool Draw ( Graphics g) {Drawcoordinate (G, Chart ["COORDINATE"]); // Painting Scarner Drawchart (G, Chart ["Chart"]); Return False;} Private Void Drawcoordinate (Graphics G, XMLNode Coo) {Drawxcoor G, COO ["XCoordinate"]); // Draw X Coordinate Drawyc OR (G, COO ["Ycoordinate"]); // Draw Y Coordinate} Private Void Drawxcoor (Graphics G, XMLNode Xcoo) {Int x = Int.Parse (Xcoo.attributes ["x"]. innertext); int y = int.parse (xcoo.attributes ["y"]. innerText); intlene = int.parse (Xcoo.attributes ["length"]. innertext) Bool arrow = BOOL.PARSE (Xcoo.attributes ["arrow"]. innerText); int stroke = int.parse (xcoo.attributes ["stroke"]. innerText); color color = color.fromname (Xcoo.attributes [XcOo.attributes "color"]. innerText); Pen Pen = new pen (color, (float) stroke); if (arrow) // is an arrow {adjustablerowcap arrow = new adjustableArrowcap ((float) (Stroke * 1.5 1.5), Float) (stroke * 1.5 2), true); pen.customedcap = arrow;} g.drawline (Pen, x, y, x length, y); // Painting Split File: // Draw Foreach (XMLNode Scale in xcoo.childnodes) {int LEN = int.parse (Scale.attributes ["Length"]. InnerText; int.com = int.parse (scale.attributes ["height"]. Innertext); int width = int. PARSE (Scale.attributes ["Width"]. InnerText); int FontSIZE = INT.PARS (Scale.attributes ["fontsize"]. InnerText); Color CLR = Color.FromName (Scale.attributes ["color"]. InnerText ); string name = scale.innertext; Pen P = new Pen (CLR, (float) width; g.drawline (p, x

LEN, Y, X LEN, Y - Height; Font Font = New Font ("Arial", (FLOAT) FONTSIZE); G.DrawString (Name, Font, New Solidbrush (CLR), (FLOAT) (X LEN - 10), (FLOAT) (Y 10));}}}} private void drawycoor (graphics g, xmlnode ycoo) {int x = int.parse (Ycoo.attributes ["x"]. InnerText); int y = int = int .PARSE (YCOO.ATTRIBUTES ["Y"]. InnerText); int length = int.parse (Ycoo.attributes ["length"]. InnerText); Bool Arrow = bool.parse (Ycoo.attributes ["arrow"]. InnerText); int stroke = int.parse (Ycoke "]. InnerText); color color = color.fromname (Ycoo.attributes [" color "]. InnerText; pen pen = new pen (color, Float); if (arrow) // Is there an arrow {adjustableArrowcap arrow = new adjustableArrowcap (FLOAT) (stroke * 1.5 3), (stroo * 1.5 3), true); pen.customedcap = Arrow;} g.drawline (Pen, X, Y, X, Y Length); // Painting Split File: // Draw Foreach (XMLNode Scale in Ycoo.childNodes) {Int Len = Int.Parse (Scale.attributes ["Length"]. innerText); int Height = int.parse (scale.attributes ["height"]. innerText); int width = int.parse (Scale.attribut ES ["Width"]. InnerText); int FontSIZE = INT.PARS (Scale.attributes ["Fontsize"]. InnerText); Color CLR = Color.FromName (Scale.attributes ["Color"]. innerText; String Name = Scale.innertext; Pen P = New Pen (CLR, (FLOAT) Width; g.drawline (P, X, Y LEN, X Height, Y LEN); Font Font = New Font ("Arial", (float); stringformat sf = new stringFormat (); sf.alignment = stringalignment.far; Rectanglef Rect = new Rectanglef (x - 100), (float) (Y LEN - 25), 90F, 50f); sf.LineAlignment = stringalignment.center;

G. DrawString (Name, Font, New Sofidbrush (CLR), RECT, SF);} }.com } private void DrawLines (Graphics g, XmlNode lines) {int Stroke = int.Parse (lines.Attributes [ "stroke"] InnerText.); Point [] points = new Point [lines.ChildNodes.Count]; Color linecolor = Color .Fromname (lines.attributes ["color"]. Innertext); for (int i = 0; i

Switch (Element.name) {CASE "text": printelement = new text (element); Break; case "table": printelement = new table (element); Break; case "linechart": // Added linechartprintelement = new Linechart (Element); Break; default: printelement = new printelement (); Break;

Replace the Table tag in the original XML file and its child tags to the next paragraph, then compile the program, the effect is as follows:

Now, our print control can print the drawline map, because we use the Abstract Factory design mode, divide the resolution of the prints and formats of the report so that this program has a very convenient expansion capability, if you need a new new The formal chart, then you need to define the label, write a parsing class, and add a CASE to this class in PASER to get it, the code inside the PrintControl does not need to be rewritten.

转载请注明原文地址:https://www.9cbs.com/read-65165.html

New Post(0)
CopyRight © 2020 All Rights Reserved
Processed: 0.051, SQL: 9