This is a sample library that uses Irony.Net to define a grammar and TerraPDF to print PDF.
PdfSharpDslCoremulti-targetsnet8.0andnet10.0;PdfSharpDsl.LanguageandPdfSharpDslCore.Generatortargetnetstandard2.0because they are loaded by the compiler.PdfSharpDslConsoleand the test project targetnet10.0.- The repository is pinned to SDK
10.0.400inglobal.json. - Version, licence, package metadata and every NuGet version are centralized in
_build/:Version.props(the single product version),Common.props(shared metadata and target-framework aliases) andPackages.props(central package management). TheDirectory.Build.props,Directory.Build.targetsandDirectory.Packages.propsfiles at the repository root only import these. Change a version or a target framework there, never in an individual.csproj. - The generator package includes its analyzer dependencies and supports clean NuGet consumer builds.
Run the full test and core coverage gate with:
powershell -NoProfile -File .\scripts\coverage.ps1The gate requires at least 90% core line coverage. See tasks.md for the migration tracker and verified results.
var parser = new Irony.Parsing.Parser(new PdfGrammar());
var parsingResult = parser.Parse(File.ReadAllText("pdfsharp.ipdf"));
if (parsingResult.HasErrors())
{
//show Error
foreach (var error in parsingResult.ParserMessages)
{
Console.Write(error.Location.ToString());
Console.Write("=>");
Console.WriteLine(error);
}
}
else
{
using var drawer = new PdfDocumentDrawer();
new PdfDrawerVisitor().Draw(drawer, parsingResult);
drawer.PublishPdf("helloworld.pdf");
}or download source code, then goto PdfSharpDslConsole and run it
dotnet run sequenceDiagram
participant yourprogram
participant PdfSharpDslCore
participant Irony
participant TerraPDF
yourprogram->>Irony: Parse file or text.
Irony -->> PdfSharpDslCore: use PdfGrammar.
Irony-->>yourprogram: Parsing result.
yourprogram->>PdfSharpDslCore: Define callback for Formula functions.
yourprogram->>PdfSharpDslCore: Draw()
PdfSharpDslCore -->>TerraPDF: publish recorded canvas commands.
PdfSharpDslCore->>yourprogram: call registered formula functions.
yourprogram-->>PdfSharpDslCore: function result.
PdfSharpDslCore->>PdfSharpDslCore: execute all instructions from source file.
PdfSharpDslCore-->>yourprogram: end of draw
It's a list of drawing "orders" follow by ';'
All coordinates are specified in 'points'
- 1 inch = 72.0 points
- 1 millimeter = 72.0 points / 25.4 ~=> 2.84 points
- 1 centimeter = 72.0 points / 2.54 ~=> 28.34 points
- a formula result can be string or double
- aritmetic/boolean operations are : +, -, /, *, %, >, <, >=, <=,==, <>, and, or
- variable reference : $VarName, and must be declared before with SET VAR VarName=Formula
- a formula is a string or a boudle value
- Boolean comparison are resolved using double, 0.0 => false, other value => true
- string comparison accept only <>, == operators
SET VAR A=2
SET VAR B=3
SET VAR CSquare=$A*$A+$B*$B
Formula can be used in :
- each number in [PointLocation, RectLocation, Width, FontSize, startAngle, sweepAngle]
- in UserDefineFunction (UDF)
- in IF condition
Formula support also customFunction
- Register a formula function in your code
var visitor = new PdfDrawerVisitor();
visitor.RegisterFormulaFunction("SUM", (args) => args.Sum(x => Convert.ToDouble(x)));- then use it in formulas
SET VAR CSquare=Sum($A*$A, $B*$B+Sum(1,2,3))
Available without registration. A function registered with RegisterFormulaFunction under the same name overrides it.
| Group | Functions |
|---|---|
| Math | Min(a,b,…), Max(a,b,…), Sum(a,b,…), Abs(x), Round(x[,digits]), Floor(x), Ceil(x), Sqrt(x), Pow(x,y) |
| String | Upper(s), Lower(s), Len(s), Substr(s,start[,len]), Replace(s,a,b), Trim(s) |
| Format | Format(value, fmt): .NET format string, invariant culture, e.g. Format(1284.5,"N2") → 1,284.50, Format(Now(),"yyyy-MM-dd") |
| Date | Now(), Today(): return a DateTime. Only usable through Format; concatenating one with + falls back to DateTime's default, culture-dependent ToString() |
| Logic | Iif(cond, a, b) |
| List | Count(list): number of items of a list |
A wrong argument count raises a PdfParserException naming the function.
TextWidth(text) and TextHeight(text[, maxWidth]) measure text in points using the current font (SET FONT), e.g. to size a box to its content:
SET VAR W=TextWidth("Total");
FILLRECT 40,100,$W+20,20;
Set by the engine, read like any other variable.
| Variable | Available | Value |
|---|---|---|
| $PAGEWIDTH | everywhere | current page width in points |
| $PAGEHEIGHT | everywhere | current page height in points |
| $PAGEINDEX | everywhere | 1-based index of the current page |
| $PAGECOUNT | everywhere (text only, see below) | total number of pages, resolved when the document is published |
| $ROWINDEX | inside ROWTEMPLATE (free or table) | 0-based index of the current iteration |
| $LASTTEMPLATEHEIGHT | after a ROWTEMPLATE | height in points of the last template, when it did not break the page |
| $CURSORY | inside FLOW | y where the next element of the flow starts |
See also the reserved UDF __ONNEWPAGE (called after each new page). It and the MASTER body can draw with their own font, brush and pen: the ones in use before the page was created are restored afterwards (variables they set stay set).
$PAGECOUNT is only known once every page has been recorded, so it only works inside TITLE/LINETEXT text, e.g. a footer set from __ONNEWPAGE:
TITLE Margin=-18 Text=("page "+$PAGEINDEX+" / "+$PAGECOUNT);
It only supports string concatenation (+); using it in arithmetic or a comparison ($PAGECOUNT-1, $PAGECOUNT>3, …) raises a clear error, since its value does not exist yet while the script runs.
# IF ... THEN ... [ELSE IF ... THEN ...]... [ELSE ...] ENDIF
IF $X > 10 THEN
SET VAR SIZE="big";
ELSE IF $X > 5 THEN
SET VAR SIZE="medium";
ELSE
SET VAR SIZE="small";
ENDIF
# FOR var=from TO to [STEP n] DO ... ENDFOR (bounds are inclusive)
FOR I=0 TO 10 STEP 5 DO ... ENDFOR # 0, 5, 10
FOR I=10 TO 0 STEP -2 DO ... ENDFOR # counts down
# WHILE condition DO ... ENDWHILE
SET VAR Y=100;
WHILE $Y < $PAGEHEIGHT-50 DO
LINE 40,$Y,300,$Y;
SET VAR Y=$Y+20;
ENDWHILE
ELSE IF(with any whitespace between the words) continues the sameIF: the whole chain has a singleENDIF, and conditions are only evaluated until one is true. Breaking change:ELSEfollowed by a nestedIFused to need twoENDIFs; to nest anIFinside anELSEnow, put something (a statement or a#comment) betweenELSEandIF.STEPis an integer formula,1by default. A negative step counts down;STEP 0raises aPdfParserException. A loop whose direction does not reachto(FOR I=5 TO 1) does not run.WHILEstops with aPdfParserExceptionafter 10 000 iterations, so a condition that never becomes false cannot hang the generation.- Loop variables and variables set in a loop stay available after it.
SET VAR ITEMS=["a","b","c"];
SET VAR FIRST=$ITEMS[0]; # indexes start at 0
SET VAR N=Count($ITEMS);
SET VAR Y=100;
FOREACH ITEM IN $ITEMS DO
LINETEXT 40,$Y Text=$ITEM;
SET VAR Y=$Y+16;
ENDFOREACH
SET VAR M=[[1,2],[3,4]];
SET VAR X=$M[1][0]; # 3
- A list literal is
[formula, formula, …]([]is an empty list); items can be any formula, including other lists. $VAR[index]reads an item; the index is a formula. It can be chained for lists of lists. It only applies to a variable (Names()[0]is not supported; store the result in a variable first). An index that is not a whole number inside the list raises aPdfParserExceptionwith the position.FOREACH var IN formula DO … ENDFOREACHvisits the items in order; the formula must give a list.- A formula function registered by the host can return any array or list (not a string): it works with
FOREACH,Countand$VAR[index]. - A list used in a text (
"items: "+$ITEMS) is shown as[a, b, c].
The host gives the script structured data before drawing:
var visitor = new PdfDrawerVisitor();
visitor.SetData("orders", orders); // any IEnumerable of objects or dictionaries
visitor.SetData("REPORTTITLE", "Q3 report"); // or a plain value: a preset variable
visitor.Draw(drawer, tree);LINETEXT 40,40 Text=$REPORTTITLE;
SET VAR Y=80;
FOREACH O IN $orders DO
LINETEXT 40,$Y Text=($O.customer+": "+Format($O.amount,"N2"));
SET VAR Y=$Y+16;
ENDFOREACH
ROWTEMPLATE Count=Count($orders) Y=200
LINETEXT 40,0 Text=$orders[$ROWINDEX].customer;
ENDROWTEMPLATE
$record.fieldreads a field: from a dictionary by key, from any other object by public property or field (case-insensitive, an exact match wins). It chains with indexes:$orders[0].customer,$order.parent.name,$o.tags[2]; a list item can be a record.- A missing field raises a
PdfParserExceptionwith the position and, when a name is close, a "Did you mean" (or the list of available fields); a field of an empty value ($o.parent.namewith no parent) raises one too. - A dictionary is a record, not a list:
FOREACHover one is an error. Any otherIEnumerableexcept a string is a list, soCount,FOREACHand$LIST[i]work on it. - Values keep their .NET type: a
DateTimefield goes throughFormat($o.date,"yyyy-MM-dd"), adecimalworks in arithmetic. SetDatanames are case-sensitive, like variables. The values are copied into the variables at eachDraw; the script can overwrite them withSET VAR.
STYLE h1
SET FONT Name="Arial" Size=20 bold;
SET BRUSH darkblue;
ENDSTYLE
USE h1;
LINETEXT 40,60 Text="Title";
- A style is a list of
SET PEN,SET BRUSH,SET HBRUSHandSET FONTstatements (noSET VAR);USE name;replays them, and they stay set afterwards like any otherSET. - Styles are hoisted like UDFs:
USEcan come before theSTYLEdefinition.USEof an unknown style raises aPdfParserException(with a "Did you mean" suggestion when a style has a similar name); defining the same style twice does too.
INCLUDE "common.ipdf";
- The statements of the file replace the
INCLUDEline, so a shared file can defineUDFs,STYLEs,MASTERs and variables (SET VAR), and can also draw. - Paths are relative to the file that contains the
INCLUDE(so nested includes work from any folder).INCLUDEis only accepted at the top level of a file, not inside a block. - A file is included once per document: a later
INCLUDEof the same file is skipped. So every file canINCLUDEthe shared files it needs (and still be drawn alone) without defining their UDFs twice. - A missing file, a circular include (
a.ipdf -> b.ipdf -> a.ipdf), a nesting deeper than 16 files or a syntax error in the included file raise aPdfParserException; the last one names the file and the line in that file. Run-time errors inside an included file (undefined variable, unknown function, list index…) sayat line 2, col 12 of file.ipdf, including from a UDF or MASTER defined there. - Relative
IMAGE Source=paths are resolved from the folder of the file that contains the statement. - The visitor resolves the main file's relative paths from its base directory (
new PdfDrawerVisitor(baseDirectory, logger), the current directory by default); the console uses the folder of the drawn file. It also works in the source generator. Since an included file is parsed when the document is drawn, the syntax ofINCLUDEd files is not checked by the firstParseof the main file. PdfSharpDslConsole/demo.ipdfis only a list ofINCLUDEs ofdemo-common.ipdf(shared UDFs, styles, master) anddemo-includes/NN-feature.ipdf, one file per feature; each of them can be drawn alone, e.g.dotnet run -- demo-includes/11-flow.ipdf.
# BARCODE x,y,w,h Type=code128 Text=formula;
SET BRUSH black;
BARCODE 40,100,260,60 Type=code128 Text="ABC-123";
- Draws a Code 128 barcode as vector bars in the current brush. The rectangle includes a quiet zone of 10 modules on each side; scale it so the bars stay wide enough for your scanner.
- Code sets B and C are used automatically (runs of 4 or more digits use C, so numeric codes are shorter). The text must be printable ASCII (32 to 126). Other characters, an empty text or a missing/zero width or height raise a
PdfParserException. - Works inside
ROWTEMPLATEand withDEBUG_RECT. No human-readable text is printed; useLINETEXTunder the bars. - QR codes are not available yet (see T25 in
tasks.md).
# CHART bar|line|pie x,y,w,h Data=list [Labels=list] [Colors=list];
CHART bar 40,100,300,200 Data=[12,30,18] Labels=["Q1","Q2","Q3"] Colors=[steelblue];
CHART line 380,100,300,200 Data=[5,-4,8,3] Labels=["a","b","c","d"] Colors=[tomato];
CHART pie 40,340,300,200 Data=[50,30,20] Labels=["Direct","Search","Referral"] Colors=[steelblue,lightgreen,gold];
- One chart in one line, drawn with the drawing primitives (
FILLRECT,LINE,FILLPIE,LINETEXT) inside the rectangle, so it works in aROWTEMPLATE, in aFLOW(with absolute coordinates) and withDEBUG_RECT. The current pen, brush and font are restored afterwards; the font family is the current one, at 8 pt. Datais a list ([12,30,18], a variable, or a host list) or a text"12,30,18".LabelsandColorsare lists too (LabelsbeforeColors, both optional).- bar and line have a value axis with round ticks (negative values are drawn below the zero line), one label per item under the axis, and the value above each bar. pie starts at the top and goes clockwise; with
Labelsa legend with the percentages is drawn at its right. - A color is a name (
steelblue, bare in a list or as a text) or"#RRGGBB"/"#AARRGGBB". When there are fewer colors than items they repeat; withoutColorsa default palette is used (alinechart uses the first color). - Errors raise a
PdfParserException: empty data, a value that is not a number, an unknown color (with a suggestion), a pie with a negative value or a zero sum, a rectangle too small for the axes. - A bare color name is now also a valid formula: it is the text of that name (
SET VAR C=red;).
# SET PEN Color Width [Style]
SET PEN black 1;
SET PEN black 1 solid;
SET PEN black 1 dot;
SET PEN black 1 dashdot;
SET PEN black 1 dashdotdot;
**Width** is one of
- number
- Formula
# SET BRUSH Color
SET BRUSH black;
**Color** is one of
- NamedColor : [color list](./README.md#named-color-list)
- HexColor : 0xRGB
- sample: 0xFFEEBB
# SET FONT FontName FontSize [FontStyle]
SET FONT Name="Arial" Size=20 bold;
FontName is one of
- string
- Formula
[FontStyle] is one of
- regular (if not specified)
- bold
- italic
- bolditalic
- underline
- strikeout
Draw text with a specified margin from top or bottom if negative.
# TITLE HAlign=HorizontalAlignment Text="text to draw"
TITLE HAlign=hcenter Text="TITLE TEST";
# TITLE [MarginTop] HorizontalAlignment "text to draw"
TITLE Margin=50 HAlign=hcenter Text="My title with margin 50";
HorizontalAlignment : left, hcenter, right [MarginTop] : margin from top if positive, margin from bottom if negative
# ELLIPSE RectLocation
ELLIPSE 5, 5, -5, -5;
# RECT RectLocation
RECT 5, 5, -5, -5;
SET BRUSH orange;
# FILLRECT RectLocation
FILLRECT 250, 100, 50,50;
SET BRUSH green;
# FILLELLIPSE RectLocation
FILLELLIPSE 250, 200, 50,50;
# LINE RectLocation
LINE 100,100, 200, 100;
here Width and Height of RectLocation is X1 and Y1
LINE, ELLIPSE, RECT use PEN (outline)
FILLRECT, FILLELLIPSE use PEN (outline) and BRUSH (fill)
RectLocation is one of
- positive number : number of point from left
- negative number : number of point from right
- formula : supports only "+ - * / ( )"
# PIE RectLocation startAngle sweepAngle
PIE 10,10,120,120 Start=0 Angle=90;
FILLPIE 10,10,120,120 Start=0 Angle=90;
# POLYGON PointLocation PointLocation PointLocation [PointLocation PointLocation PointLocation ...]
POLYGON 300,300, 350,320, 330,350, 240,240;
FILLPOLYGON 100,100, 150,120, 130,150, 240,40;
# MOVETO PointLocation
# LINETO PointLocation
MOVETO 300, 200;
LINETO 400, 200;
LINETO 400, 220;
LINETO 300, 200;
# NEWPAGE [PageSize] [PageOrientation];
NEWPAGE ;
NEWPAGE A4 portrait;
[PageSize] is one of
- A0, A1, A2, A3, A4, A5, A6, B0, B1, B2, B3, B4, B5, Crown, Demy, DoubleDemy, Elephant, Executive, Folio, Foolscap, GovernmentLetter, LargePost, Ledger, Legal, Letter, Medium, Post, QuadDemy, Quarto, RA0, RA1, RA2, RA3, RA4, RA5, Royal, Size10x14, Statement, STMT, Tabloid, Undefined
[PageSize] is one of
- portrait, landscape
# MASTER name [MarginTop=formula]
# statements (usually TITLE, for a header/footer)
# ENDMASTER
MASTER report MarginTop=60
TITLE Margin=20 Text="ACME report";
TITLE Margin=-18 Text=("page "+$PAGEINDEX+" / "+$PAGECOUNT);
ENDMASTER
NEWPAGE A4 portrait Master=report;
- Masters are hoisted like UDFs, so
Master=namecan reference one defined later in the file. - The master's statements run, right after
__ONNEWPAGE, on the page aNEWPAGE ... Master=name;creates and on any page aROWTEMPLATEpage break creates from it. A laterNEWPAGEwithoutMaster=has no master, even if the previous page had one — give itMaster=nameagain to keep using it. MarginTopbecomes the defaultNewPageTopMarginfor aROWTEMPLATEthat doesn't specify its own (see ROWTEMPLATE), so content doesn't start under the master's header.NEWPAGE Master=unknown;raises aPdfParserException.
Place content top to bottom without computing y.
# FLOW [Margin=formula] [Top=formula]
# PARAGRAPH [HAlign=left|right|hcenter] Text=formula;
# SPACE formula;
# IMAGE x,y,w,h ...; # x relative to the left margin, y relative to the cursor
# TABLE x,y ... ENDTABLE # same
# ENDFLOW
FLOW Margin=40
SET FONT Name="Arial" Size=10 regular;
PARAGRAPH Text="A long text that wraps to the flow width...";
SPACE 12;
PARAGRAPH HAlign=right Text=("Now at y = "+$CURSORY);
ENDFLOW
- The flow is
PageWidth - 2*Marginwide (Margindefaults to 36) and stopsMarginabove the bottom of the page. Topis where the first element starts on the first page; it defaults to the active master'sMarginTop, or elseMargin. Pages created by the flow start there too.PARAGRAPHwraps its text (current font and brush) and moves the cursor down. A paragraph taller than the space left is split between lines, and continues at the top of the next page.SPACEmoves the cursor down (or up if negative) and draws nothing.IMAGEandTABLEkeep their syntax, butx,yare relative to the left margin and the cursor. An image that does not fit moves to the next page; a table breaks between rows (seeTop), and the cursor ends below its last row.- A page break made by the flow calls
NEWPAGEunder the hood, so the current MASTER and__ONNEWPAGEapply to the new page. $CURSORYis only readable inside a flow.PARAGRAPH/SPACEoutside a flow, and aFLOWinside aFLOW, raise aPdfParserException. Other statements can be used in a flow but do not move the cursor.- FLOW works in page points: don't combine it with
VIEWSIZE.
The console (and PdfDslDiagnostics.FormatParseErrors(parseTree) for hosts) reports errors with line and column, and a suggestion when a name looks like a typo:
Unknown instruction 'LINETXT' at line 12, col 1. Did you mean 'LINETEXT'?
Missing ';' after '1' at line 3, col 14.
Missing 'ENDFOR' for 'FOR' opened at line 5, col 1.
'ENDFOR' at line 9, col 1 does not match 'IF' opened at line 6, col 1. Expected 'ENDIF'.
Run-time errors carry a position too: Variable '$TOTL' is not defined at line 30, col 21. Did you mean '$TOTAL'? and Unknown function 'Uppr' at line 4, col 21. Did you mean 'UPPER'?. Both are PdfParserExceptions (an undefined variable used to be an ArgumentOutOfRangeException).
# IMAGE PointLocation Source=ImageFilePath
IMAGE 100,100 Source="./imageTest.jpg";
# IMAGE PointLocation Data=Base64 encoded image
IMAGE 100,100 Source="data:image/...";
# IMAGE PointLocation,width,height width_height_unit ImageFilePath
IMAGE 320,100,34,34 point Source="./imageTest.jpg";
# IMAGE PointLocation,width,height width_height_unit [cropping] ImageFilePath
IMAGE 100,320,50,50 pixel crop Source="C:\\Samples\\imageTest.jpg";
IMAGE 100,320,50,50 pixel crop Source="C:/Samples/imageTest.jpg";
ImageFilePath : path can be relative or absolute
width_height_unit : 'point' or 'pixel'
- when specified image is scale to provided rectangle
[cropping] : don't scale but crop image from provided rectangle
# LINETEXT PointOrRect [hAlign] [vAlign] [Orientation] Text="text"
LINETEXT 42,100 Text="Horizontal text"
LINETEXT 42,100 vertical Text="Horizontal text"
LINETEXT 42,100 left bottom vertical Text="Horizontal text";
[hAlign] is one of
- left, right, hcenter
[vAlign] is one of
- top, bottom, vcenter
[Orientation] is one of
- horizontal, vertical
With a rect location (x,y,w,h), two options handle text that doesn't fit:
LINETEXT 40,100,120,20 Fit=shrink Text="This is a long label";
LINETEXT 40,100,120,20 Overflow=ellipsis Text="This is a long label";
Fit=shrinkreduces the font size (down to 4pt) until the text fits the rect.Overflow=ellipsistruncates the last line that fits the rect's height and appends….
# TEXT PointOrRect [MaxWidth=formula] Text="text"
TEXT 42,100 Text="Horizontal text"
TEXT 42,100 MaxWidth=50 Text="Horizontal text"
- specify MaxWidth to wrap text on multi-lines
- this 'TEXT' cannot be align
# ROWTEMPLATE Count=formula Y=formula [BorderSize=formula]
# ENDROWTEMPLATE
ROWTEMPLATE Count=3 Y=300 BorderSize=5
# top line under border
LINE 0,0,$PAGEWIDTH-20, 0;
LINE 0,0,50, 50;
# bottom line under border
LINE 0,50,$PAGEWIDTH-20, 50;
ENDROWTEMPLATE
# rect include borders
RECT 0,300, $PAGEWIDTH-20, $LASTTEMPLATEHEIGHT
This statement is like a table, but only for row
- foreach loop the start point is 0,0
- you can access to variable '$ROWINDEX' inside template
- after template you can access to '$LASTTEMPLATEHEIGHT' to know the height(in point) of the previous template
[BorderSize]
- is to add space between each loop
- a space is added on top and at bottom when specified
Udf is like a method in C#, where you can group instructions and reuse multiple times
UDF MyUdf()
LINETEXT 100,100 Text="Horizontal text"
ENDUDF
UDF MyUdf1(X,Y)
LINETEXT $X,$Y Text="Horizontal text"
ENDUDF
udf with parameters
UDF HEXAGONE(X, Y, T)
SET VAR SQRT3=1.7320;
LINETEXT $X-$T/2, $Y-$T*$SQRT3/2 HAlign=left VAlign=bottom Text="MOVETO/LINETO";
MOVETO $X-$T/2, $Y-$T*$SQRT3/2;
LINETO $X+$T/2, $Y-$T*$SQRT3/2;
LINETEXT $X+$T/2,$Y-$T*$SQRT3/2 HAlign=left VAlign=bottom Orientation=60 Text="LINETO";
LINETO $X+$T, $Y+0;
LINETEXT $X+$T, $Y+0 HAlign=left VAlign=bottom Orientation=120 Text="LINETO";
LINETO $X+$T/2, $Y+$T*$SQRT3/2;
LINETEXT $X+$T/2, $Y+$T*$SQRT3/2 HAlign=left VAlign=bottom Orientation=$PI Text="LINETO";
LINETO $X-$T/2, $Y+$T*$SQRT3/2;
LINETEXT $X-$T/2, $Y+$T*$SQRT3/2 HAlign=left VAlign=bottom Orientation=240 Text="LINETO";
LINETO $X-$T, $Y+0;
LINETEXT $X-$T, $Y+0 HAlign=left VAlign=bottom Orientation=300 Text="LINETO";
LINETO $X-$T/2, $Y-$T*$SQRT3/2;
ENDUDF
introduce in 1.0.4
UDF __ONENEWPAGE()
# called on each new page
# add here your custom draw (example draw PageIndex)
LINETEXT ($PAGEWIDTH/2),$PAGEHEIGHT HAlign=hcenter VAlign=bottom Text=$PAGEINDEX;
ENDUDF
if you redefine 'MyUdf' in C# you can control
var visitor = new PdfDrawerVisitor();
visitor.RegisterCustomUdf("MyUdf", (drawer, argNames, argValues) => {
//....
//this udf is called before udf define in '.ipdf' file
drawer.DrawLineText("Horizontal Text", 100,100,null,null,
XStringAlignment.Near, XLineAlignment.Far,
new TextOrientation(){Orientation=TextOrientationEnum.Horizontal, Angle = 0});
//return true to override, false to execute also udf define un .ipdf
return true;
});UDF DOUBLE(X)
RETURN $X*2;
ENDUDF
SET VAR Y=DOUBLE(21); # 42
LINETEXT 40,40 Text=("6! = "+FACT(6)); # a UDF may be recursive
RETURN formula;ends the UDF (even from inside anIFor a loop) and gives the value. It is only allowed in a UDF body; anywhere else raises aPdfParserException.- A formula calls a UDF like any function (
NAME(args), the name is not case-sensitive there). Registered functions and built-ins come first: a UDF with the same name is not called from a formula (it still is byCALL). - A UDF used in a formula must
RETURNa value, and get exactly its parameters. It may also draw (RECT,LINETEXT, ...):SET VAR NEXTY=BOX(45,$Y,"label");can draw a box and return the next y. Its variables stay local, as withCALL. CALL name(...)still works, and aRETURNin the body ends it early (the value is ignored).- A UDF called more than 256 levels deep (endless recursion) raises a
PdfParserException.
CALL MyUdf();
# udf with parameters
CALL MyUdf1(100,100);
each parameter can be a Formula
DEBUGOPTIONS [GLOBAL|PAGE] Option1 [, Option2];
Scope
- GLOBAL (default when omitted) : options apply to the whole document, wherever the statement is written
- PAGE : options apply from the statement to the end of the current page, they are reset on each new page (NEWPAGE or ROWTEMPLATE page break)
DEBUGOPTIONS DEBUG_TEXT;
NEWPAGE;
# rule only on this page
DEBUGOPTIONS PAGE DEBUG_RULE;
Available options
- DEBUG_TEXT : shows red rect around texts
- DEBUG_RECT : shows red rect around each figure (rect, ellipse, pie, polygon)
- DEBUG_IMAGE : shows red rect around images
- DEBUG_RULE : shows rule on each page
- DEBUG_ALL : all of the above
- DEBUG_GRID : shows a light grid every 50 points, labelled with its coordinates, to help place elements
- DEBUG_ROWTEMPLATE : shows red rect around each iteration and index number of each at topleft rectangle
- text format is "{level}.{index}", where level is > 0 when ROWTEMPLATE is part of another ROWTEMPALTE
aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkturquoise darkviolet deeppink deepskyblue dimgray dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen lightpink lightsalmon lightseagreen lightskyblue lightslategray lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray snow springgreen steelblue tan teal thistle tomato transparent turquoise violet wheat white whitesmoke yellow yellowgreen
this package is build on top of
-
PDF :
- TerraPDF : https://www.nuget.org/packages/TerraPDF
-
Parsers :
You can generate C# from PDF DSL, in order to have "hard coded" PDF. You can generate C# code of UDF (user define function)
- is not yet available