richard-scryber / scryber.core

Scryber.Core is a dotnet 6 html to pdf engine written entirely in C# for creating beautiful flowing documents from html templates including css styles, object data binding and svg drawing.
Other
194 stars 31 forks source link

Inline CSS seems to be ignored when html string content is parsed as a component. #56

Open jcvitug opened 3 years ago

jcvitug commented 3 years ago

The reports I'm generating requires me to parse through an html string with inline CSS styles. I tried achieving this by parsing through the string after the data is bound to the table cell and storing it as a component, then adding it to the contents of the table cell.

Template

            <tbody id="TBody">
                <!-- and a template for the table rows looping over each of the items -->
                <template data-bind="{@:Model.Items}">
                    <!-- Event on-databound will generate columns dynamically after data has been binded -->
                    <tr on-databound="generate-rows">
                    </tr>
                    <template data-bind="{@:.notes}">
                        <tr style="background-color:#dedede;">
                            <td>
                                {@:.createdOn}
                            </td>
                            <!-- Adjusts colspan according to number of columns and parses through html string -->
                            <td on-databound="parsed-note">
                            </td>
                        </tr>
                    </template>
                </template>
            </tbody>

Controller

        [PDFAction("parsed-note")]
        public void ParseNoteContent(object sender, PDFDataBindEventArgs args)
        {
            var cell = sender as HTMLTableCell;
            var columnParam = this.Document.Params["Model"].GetType().GetProperty("Columns");
            var columns = columnParam.GetValue(this.Document.Params["Model"], null) as object[];
            var columnCount = columns.Length;
            cell.CellColumnSpan = columnCount - 1;
            var currentData = args.Context.DataStack.Current as JObject;
            var cellContent = currentData["note"].ToString();
            //test string for html
            Regex tagRegex = new Regex(@"<\s*([^ >]+)[^>]*>.*?<\s*/\s*\1\s*>");
            if (tagRegex.IsMatch(cellContent))
            {
                var outerTagPosition = cellContent.IndexOf(">");
                cellContent = cellContent.Replace("&nbsp;", " ");
                var formattedContent = cellContent.Insert(outerTagPosition, " xmlns='http://www.w3.org/1999/xhtml'");
                var parsedNote = this.Document.ParseTemplate(this.Document, new System.IO.StringReader(formattedContent)) as Component;
                args.Context.TraceLog.Add(TraceLevel.Message, "Updated Note", formattedContent);
                cell.Contents.Add(parsedNote);
            }
            else
            {
                cell.Contents.Add(new TextLiteral() { Text = cellContent });
            }
        }

Example of an html string stored as a note to parse

<p><span style="background-color:rgb(255,255,255);color:rgb(0,0,0);">*PRIORITY* Case approved with Penalty of 45K, working on reducing penalty.</span></p><p>We have not exhausted all options! By way of reminder, we have two matters pending.</p><p>&nbsp;</p>

The problem is that the inline CSS seems to be ignored. image Desired outcome image

Is there a way to do this with the css included?

richard-scryber commented 3 years ago

Hi Jethro

Thanks for this, looks amazing stuff. Really like that controllers are proving valuable.

I think you have hit a limitation of the current version of the engine. We don't (at the moment) support background colours, along with borders, padding and margins on inline components. It's just complex for multiline scenarios, leading, partial lines etc.

However, if it works consistently for you, the you could add a class name to your bound td and then make the span a block.

<td class='notes' on-databound='parsed-note' >
</td>

And with your css

td.notes p>span {
   display:block;
}

This would force every inner p>span to be a block which might cause an issue, but would at least support the background colours.

I will add the span block styles to the backlog, but it might be a while.