Converts Quill's Delta format to XML or HTML (insert ops only) with properly nested lists. This is a c# port of the javascript version by nozer.
Install with package manager:
Install-Package Quill.Delta
or with nuget:
nuget install Quill.Delta
Or with dotnet:
dotnet add package Quill.Delta
Quill.Delta can also be installed from nuget.org.
using Quill.Delta;
using Newtonsoft.Json.Linq;
var deltaOps = JArray.Parse(@"[
{insert: ""Hello\n""},
{insert: ""This is colorful"", attributes: {color: '#f00'}}
]");
var htmlConverter = new HtmlConverter(deltaOps);
string html = htmlConverter.Convert();
var xmlConverter = new XmlConverter(deltaOps);
XmlDocument xml = xmlConverter.Convert();
HtmlConverter
and XmlConverter
take an configuration options object as the second (optional) argument. The HtmlConverterOptions
and XmlConverterOptions
objects share the following properties:
Option | Default | Description |
---|---|---|
ParagraphTag | "p" | Custom tag to wrap inline html elements |
ClassPrefix | "ql" | A css class name to prefix class generating styles such as size , font , etc. |
InlineStyles | false | If true, use inline styles instead of classes |
MultiLineBlockquote | true | Instead of rendering multiple blockquote elements for quotes that are consecutive and have same styles(align , indent , and direction ), it renders them into only one |
MultiLineHeader | true | Same deal as multiLineBlockquote for headers |
MultiLineCodeblock | true | Same deal as multiLineBlockquote for code-blocks |
MultiLineParagraph | true | Set to false to generate a new paragraph tag after each enter press (new line) |
LinkRel | "" | Specifies a value to put on the rel attr on links |
LinkTarget | "_blank" | Specifies target for all links; use '' (empty string) to not generate target attribute. This can be overridden by an individual link op by specifiying the target with a value in the respective op's attributes. |
AllowBackgroundClasses | false | If true, css classes will be added for background attr |
HtmlConverter
has the following extra properties:
Option | Default | Description |
---|---|---|
EncodeHtml | true | If true, <, >, /, ', ", & characters in content will be encoded. |
CustomRenderer | null | A function that will be called to render custom (unknown) delta op to html (the default rendering code skips them) |
BeforeRenderer | null | A function that will be called before rendering the current delta op. If it returns a value then this is used in place of the default rendering result. |
AfterRenderer | null | A function that will be called after rendering the current delta op with the op and result. It should return the result (with optional modifications). |
XmlConverter
has the following extra properties:
Option | Default | Description |
---|---|---|
RootNodeTag | "template" | Specifies the root node of the resulting xml document |
CustomRenderer | null | A function that will be called to render custom (unknown) delta op to xml |
BeforeRenderer | null | A function that will be called before rendering the current delta op. If it returns a value then this is used in place of the default rendering result. |
AfterRenderer | null | A function that will be called after rendering the current delta op with the op and result. It should return the result (with optional modifications). |
You can customize the rendering of Quill formats by registering to the render events before calling the Convert()
method.
There are BeforeRender
and AfterRender
events and they are called multiple times before and after rendering each group. A group is one of:
- continuous sets of inline elements
- a video element
- list elements
- block elements (header, code-block, blockquote, align, indent, and direction)
BeforeRender
event is called with raw operation objects for you to generate and return your own html. If you return an empty value, the system will return its own generated html.
AfterRender
event is called with generated xml/html for you to inspect, maybe make some changes and return your modified or original html.
Following shows the parameter formats for beforeRender
event:
groupType | data |
---|---|
GroupType.Video | {op: op object } |
GroupType.Block | {op: op object : ops: Array<op object >} |
GroupType.List | {items: Array<{item: block , innerList: list or null }> } |
GroupType.InlineGroup | {ops: Array<op object >} |
op object
will have the following format:
{
insert: {
type: '', // one of 'text' | 'image' | 'video' | 'formula',
value: '' // some string value
},
attributes: {
// ... quill delta attributes
}
}
If you are rendering to HTML that you intend to include in an email, using classes and a style sheet are not recommended, as not all browsers support style sheets. Quill.Delta supports rendering inline styles instead. The easiest way to enable this is to pass the option InlineStyles: new InlineStyles()
.
You can customize styles by setting the properties of the InlineStyles
object:
var fontDic = new Dictionary<string, string>() {
{ "serif": "font-family: Georgia, Times New Roman, serif" },
{ "monospace": "font-family: Monaco, Courier New, monospace" }
}
var sizeDic = new Dictionary<string, string>() {
{ "small": "font-size: 0.75em" },
{ "large": "font-size: 1.5em" },
{ "huge": "font-size: 2.5em" }
};
new InlineStyles {
Font = InlineStyles.MakeLookup(fontDic),
Size = InlineStyles.MakeLookup(sizeDic),
Indent = (value, op) =>
{
int indentSize = Int32.Parse(value) * 3;
var side = op.Attributes != null &&
op.Attributes.Direction == DirectionType.Rtl ? "right" : "left";
return "padding-" + side + ":" + indentSize + "em";
},
Direction = (value, op) =>
{
if (value == "rtl")
{
var hasAlign = op.Attributes != null && op.Attributes.Align.HasValue;
return $"direction:rtl{(hasAlign ? "" : "; text-align:inherit")}";
}
return null;
}
};
Keys to this object are the names of attributes from Quill. The values are either a simple lookup table (like in the 'font' example above) used to map values to styles, or a fn(value, op)
which returns a style string.
You need to tell system how to render your custom blot by suppliing the CustomRenderer handler in the XmlConverterOptions
or HtmlConverterOptions
.
If you would like your custom blot to be rendered as a block (not inside another block or grouped as part of inlines), then add renderAsBlock: true
to its attributes.
Example:
using Newtonsoft.Json.Linq;
using Quill.Delta;
var ops = JArray.Parse(@"[
{insert: {'my-blot': {id: 2, text: 'xyz'}}, attributes: {renderAsBlock: true|false}}
]");
var opts = new HtmlConverterOptions
{
// customOp is your custom blot op
// contextOp is the block op that wraps this op, if any.
// If, for example, your custom blot is located inside a list item,
// then contextOp would provide that op.
CustomRenderer = (DeltaInsertOp op, DeltaInsertOp contextOp)
{
var insert = (InsertDataCustom)op.Insert;
if (insert.CustomType == "my-blot")
{
var val = (JObject)insert.Value;
return $"<span id=\"{val["id"]}\">{val["text"]}</span>";
}
else
{
return "Unmanaged custom blot!";
}
}
};
var converter = new HtmlConverter(ops, opts);
string html = converter.Convert();
customOp object
will have the following format:
{
insert: {
type: string // whatever you specified as key for insert, in above example: 'my-blot'
value: any // value for the custom blot
},
attributes: {
// ... any attributes custom blot may have
}
}
If you want to do the full rendering yourself, you can do so by getting the processed & grouped ops.
var groupedOps = converter.GetGroupedOps();
Each element in groupedOps array will be an instance of the following types:
type | properties |
---|---|
InlineGroup |
Ops: IList<DeltaInsertOp> |
VideoItem |
Op: DeltaInsertOp |
BlockGroup |
Op: DeltaInsertOp , Ops: IList<DeltaInsertOp> |
ListGroup |
Items: IList<ListItem> |
ListItem: { Item: BlockGroup , InnerList: ListGroup } |
|
BlotBlock |
Op: DeltaInsertOp |
BlotBlock
represents custom blots with renderAsBlock:true
property pair in its attributes
See above for DeltaInsertOp
properties.
Hacking on Quill.Delta
is easy! To quickly get started clone the repo:
$ git clone https://github.com/blushingpenguin/Quill.Delta.git
$ cd Quill.Delta
To compile the code and run the tests just open the solution in Visual Studio 2017 Community Edition. To generate a code coverage report run cover.bat from the solution directory.