Referencing external css file with XMLWorker - pdf-generation

I have switched from HtmlWorker to XMLWorker in order to take advantage of the use of external css files. Besides, in this way, I avoid the use of inline styling.
This is currently my code:
public ActionResult ViewPdf(object model)
{
var doc = new Document(PageSize.A3, 10f, 1f, 10f, 30f);
var memStream = new MemoryStream();
var writer = PdfWriter.GetInstance(doc, memStream);
writer.CloseStream = false;
doc.Open();
var xmltext = RenderActionResultToString(View(model));
var htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(Tags.GetHtmlTagProcessorFactory());
var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
cssResolver.AddCssFile(Server.MapPath("~/Content/StyleSheet1.css"), true);
var pipeline = new CssResolverPipeline(cssResolver,
new HtmlPipeline(htmlContext, new PdfWriterPipeline(doc, writer)));
var worker = new XMLWorker(pipeline, true);
var parser = new XMLParser(worker);
using (var sr = new StringReader(xmltext))
{
parser.Parse(sr);
}
doc.Close();
var buf = new byte[memStream.Position];
memStream.Position = 0;
memStream.Read(buf, 0, buf.Length);
return new BinaryContentResult(buf, "application/pdf");
}
RenderActionResultToString(View(model)) is giving me the string I want to parse into pdf. This all works fine. The problem is with the styles. If I use inline styling such as:
<table style ="width: 400px">
, everything works like a charm. But if I use something like:
<table class = "foo">
, and reference a css file(StyleSheet1.css) like this:
.foo {
width: 400px;
}
, it doesn't work...
I tried referencing the stylesheet in the string I am parsing, like this in my view:
<head>
<link href ="#Server.MapPath("~/Content/StyleSheet1.css")" rel ="stylesheet" type="text/css"/>
</head>
I inserted the link to the css file just as the documentation states, inside the head tags.
Thanks in Advance

Related

Umbraco problem getting chilld properties

I have Umbraco Content List containing multiple Umbraco Content pages. When I try to find some content page properties like Page Title or Heading or bodyText I'm getting "The function evaluation requires all threads to run.". Here is my code.
#using Umbraco.Web
#using Umbraco.Web.Mvc
#{
Layout = "T";
}
#{
var UContentList = Model.Content.Children;
var SContentList = new List<MyProgram.Models.MyContent>();
foreach (var uContent in UContentList)
{
var SContent = new IMyProgram.Models.MyContent(
uContent.pageTitle.ToString(),
uContent.contentExcerpt.ToString()
);
SContentList.Add(SContent);
}
Please help how do I get the values from these properties
I've managed to do it. First need to debug and do this: https://blogs.msdn.microsoft.com/eliofek/2012/12/12/why-do-we-get-the-function-evaluation-requires-all-threads-to-run/
Then here is the source:
#inherits UmbracoTemplatePage
#using Umbraco.Web
#using Umbraco.Web.Mvc
#using ContentModels = Umbraco.Web.PublishedContentModels;
#{
/**/
Layout = "";
}
#{
var UContentList = Model.Content.Children();
var SContentList = new List<MyProgram.Models.MyContent>();
foreach (var uContent in UContentList)
{
var SContent = new MyProgram.Models.MyContent(
uContent.GetPropertyValue("pageTitle").ToString(),
uContent.GetPropertyValue("contentExcerpt").ToString(),
uContent.Url.ToString()
);
SContentList.Add(SContent);
}
The difference is the #inherits and #using ContentModels.

Kendo TreeView does not generate data attributes for custom template

I am using Kendo.Mvc.UI.TreeViewItemModel to build kendo treeview. Othe than Id and Text property this class also has HtmlAttributes property of type IDictionary<string, string>
I can add any additional data into HtmlAttributes property while building model, and kendo would render these Html Attributes as html data attribute on tree node.
public IEnumerable<TreeViewItemModel> BuildTreeView(IEnumerable<MyGroup> groups)
{
var nodes = new List<TreeViewItemModel>();
foreach (var grp in groups)
{
var root = new TreeViewItemModel()
{
Id = "0",
Text = grp.Key,
HasChildren = grp.Any()
};
foreach (var item in grp)
{
var dictionary = new Dictionary<string, string>();
dictionary.Add("data-template-id",item.TemplateID.ToString());
dictionary.Add("data-filename", item.FileName);
root.Items.Add(new TreeViewItemModel()
{
Text = item.DisplayText,
HasChildren = false,
HtmlAttributes = dictionary
});
}
nodes.Add(root);
}
return nodes;
}
cshtml
#(Html.Kendo().TreeView()
.Name("MyTreeView")
.DataTextField("Text")
.BindTo(Model.Items))
and then in JS i can access this data
var _treeView = $("#MyTreeView").getKendoTreeView();
var htmlItem = _treeView.select();
var templateid = htmlItem.data("template-id");
var filename = htmlItem.data("filename");
This is working fine as long as im using kendo's default template. However if i use custom template then Kendo does not generate data attributes
#(Html.Kendo().TreeView()
.Name("MyTreeView")
.TemplateId("treeViewItemTemplate")
.BindTo(Model.Items))
<script type="text/x-kendo-tmpl" id="treeViewItemTemplate">
<span>#=item.text#</span>
# if (!item.items){#
<span class="glyphicon glyphicon-plus-sign" aria-hidden=true></span>
#}#
</script>
now when the page renders and using F12 if look at the tree node ( which is li tag) i don't see any data attributes

Load & Display Image in ActionScript

I am typing with very green fingers, so, please excuse the question. Learning from examples, I have been trying to load and display an image in ActionScript. This is from the original example that I am working on:
...
...
var bmd2:BitmapData = bmd.clone();
var infoBM:Bitmap = new Bitmap(bmd2);
var back:MovieClip = new MovieClip();
back.name = "back";
back.rotationX = -90;
var bSurface:MovieClip = new MovieClip();
bSurface.y = int(...
bSurface.z = int(...
bSurface.addChild(infoBM);
var overlay:Sprite = new Sprite();
overlay.graphics.beginFill(...);
overlay.graphics.drawRect(0, 0, bSurface.width, bSurface.height);
overlay.graphics.endFill();
bSurface.addChild(overlay);
back.addChild(bSurface);
var tf:TextField = new TextField();
tf...
tf...
tf...
bSurface.addChild(tf);
...
...
I am trying to place an image onto the bSurface instead of the text block, as above. So far, I've come up with:
var iSprite:Sprite = new Sprite();
var iMatrix:Matrix = new Matrix();
var bmData:BitmapData;
bmData = new BitmapData(surface.width, surface.height, true, 123);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("c:\pixlr.png"));
function onComplete (event:Event):void
{
bmData = Bitmap(LoaderInfo(event.target).content).bitmapData;
}
iMatrix.tx = 0;
iMatrix.ty = 0;
iSprite.graphics.beginBitmapFill(bmData, iMatrix, false, true);
iSprite.graphics.drawRect(0, 0, surface.width, surface.height);
iSprite.graphics.endFill();
surface.addChild(iSprite);
...and:
var bmData:BitmapData;
bmData = new BitmapData(surface.width, surface.height, true, 123);
var loader:Loader = new Loader();
var location:URLRequest = new URLRequest("c:\pixlr.png");
loader.load(location);
bmData = Bitmap(loader.content).bitmapData;
surface.addChild(bmData);
but to no success. I've been failing miserably for days now, and would greatly appreciate some help.
Thanking you in advance,
Sofia
You're making this more complicated than it needs to be. A Loader is a DisplayObject. So when you want to load an image you can simply do this:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("c:\pixlr.png"));
addChild(loader);
You can use the onComplete event to handle any sizing or positioning, or fading in you might want to do.

How to display video from xml file?

Hi am using xml file given below,how can i get videos from xml file?
<Category name="Videos">
<article articleid="68">
<videourl>
<iframe src="http://player.vimeo.com/video/52375409?fullscreen=0" width="500" height="298" frameborder="0"></iframe>
</videourl>
</article>
</Category>
My Code is
XDocument loadedData = XDocument.Load("CountriesXML.xml");
var data = from query in loadedData.Descendants("Country")
select new CountryData
{
url = (string)query.Element("videourl").Elements("iframe").Single().Attribute("src").Value,
};
countryList = data.ToList();
but i got NullReferenceException error
var xdoc = XDocument.Load("CountriesXML.xml");
var videos = from f in xdoc.Descendants("iframe")
select new {
Src = (string)f.Attribute("src"),
Width = (int)f.Attribute("width"),
Height = (int)f.Attribute("height")
};
Or with your updated code:
var xdoc = XDocument.Load("CountriesXML.xml");
var data = from c in xdoc.Descendants("Category") // you have Category element
select new CountryData {
url = (string)c.Element("article") // there is also article element
.Element("videourl")
.Elements("iframe")
.Single().Attribute("src")
};

How to convert img url to BASE64 string in HTML on one method chain by using LINQ or Rx

I found I could generate XDocument object from html by using SgmlReader.SL.
https://bitbucket.org/neuecc/sgmlreader.sl/
The code is like this.
public XDocument Html(TextReader reader)
{
XDocument xml;
using (var sgmlReader = new SgmlReader { DocType = "HTML", CaseFolding = CaseFolding.ToLower, InputStream = reader })
{
xml = XDocument.Load(sgmlReader);
}
return xml;
}
Also we can get src attributes of img tags from the XDocument object.
var ns = xml.Root.Name.Namespace;
var imgQuery = xml.Root.Descendants(ns + "img")
.Select(e => new
{
Link = e.Attribute("src").Value
});
And, we can download and convert stream data of image to BASE64 string.
public static string base64String;
WebClient wc = new WebClient();
wc.OpenReadAsync(new Uri(url)); //image url from src attribute
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
byte[] buf = new byte[32768];
int read = e.Result.Read(buf, 0, buf.Length);
if (read > 0)
{
ms.Write(buf, 0, read);
}
else { break; }
}
byte[] imageBytes = ms.ToArray();
base64String = Convert.ToBase64String(imageBytes);
}
}
So, What I'd like to do is bellow steps. I'd like to do bellow steps in one method chain like LINQ or Reactive Extensions.
Get src attributes of img tags from XDocument object.
Get image datas from urls.
Generate BASE64 string from image datas.
Replace src attributes by BASE64 string.
The simplest source and output are here.
Before
<html>
<head>
</head>
<body>
<img src='http://image.com/image.jpg' />
<img src='http://image.com/image2.png' />
</body>
</html>
After
<html>
<head>
</head>
<body>
<img src='data:image/jpg;base64,iVBORw...' />
<img src='data:image/png;base64,iSDoske...' />
</body>
</html>
Does anyone know the solution for this?
I'd like to ask experts.
Both LINQ and Rx are designed to promote transformations that result in new objects, not ones that modify existing objects, but this is still doable. You have already done the first step, breaking the task into parts. The next step is to make composable functions that implement those steps.
1) You mostly have this one already, but we should probably keep the elements around to update later.
public IEnumerable<XElement> GetImages(XDocument document)
{
var ns = document.Root.Name.Namespace;
return document.Root.Descendants(ns + "img");
}
2) This seems to be where you have hit a wall from the composability point of view. To start, lets make a FromEventAsyncPattern observable generator. There are already ones for the Begin/End async pattern and standard events, so this will come out somewhere in between.
public IObservable<TEventArgs> FromEventAsyncPattern<TDelegate, TEventArgs>
(Action method, Action<TDelegate> addHandler, Action<TDelegate> removeHandler
) where TEventArgs : EventArgs
{
return Observable.Create<TEventArgs>(
obs =>
{
//subscribe to the handler before starting the method
var ret = Observable.FromEventPattern<TDelegate, TEventArgs>(addHandler, removeHandler)
.Select(ep => ep.EventArgs)
.Take(1) //do this so the observable completes
.Subscribe(obs);
method(); //start the async operation
return ret;
}
);
}
Now we can use this method to turn the downloads into observables. Based on your usage, I think you could also use DownloadDataAsync on the WebClient instead.
public IObservable<byte[]> DownloadAsync(Uri address)
{
return Observable.Using(
() => new System.Net.WebClient(),
wc =>
{
return FromEventAsyncPattern<System.Net.DownloadDataCompletedEventHandler,
System.Net.DownloadDataCompletedEventArgs>
(() => wc.DownloadDataAsync(address),
h => wc.DownloadDataCompleted += h,
h => wc.DownloadDataCompleted -= h
)
.Select(e => e.Result);
//for robustness, you should probably check the error and cancelled
//properties instead of assuming it finished like I am here.
});
}
EDIT: As per your comment, you appear to be using Silverlight, where WebClient is not IDisposable and does not have the method I was using. To deal with that, try something like:
public IObservable<byte[]> DownloadAsync(Uri address)
{
var wc = new System.Net.WebClient();
var eap = FromEventAsyncPattern<OpenReadCompletedEventHandler,
OpenReadCompletedEventArgs>(
() => wc.OpenReadAsync(address),
h => wc.OpenReadCompleted += h,
h => wc.OpenReadCompleted -= h);
return from e in eap
from b in e.Result.ReadAsync()
select b;
}
You will need to find an implementation of ReadAsync to read the stream. You should be able to find one pretty easily, and the post was long enough already so I left it out.
3 & 4) Now we are ready to put it all together and update the elements. Since step 3 is so simple, I'll just merge it in with step 4.
public IObservable<Unit> ReplaceImageLinks(XDocument document)
{
return (from element in GetImages(document)
let address = new Uri(element.Attribute("src").Value)
select (From data in DownloadAsync(address)
Select Convert.ToBase64String(data)
).Do(base64 => element.Attribute("src").Value = base64)
).Merge()
.IgnoreElements()
.Select(s => Unit.Default);
//select doesn't really do anything as IgnoreElements eats all
//the values, but it is needed to change the type of the observable.
//Task may be more appropriate here.
}

Resources