css file not being found by thymeleaf - servlets

I try to do simple servlet that generates pdf file based on HTML template. I try to use Thymeleaf and FlyingSaucer, as in example
in my template.hmtl i have style delcaration as follow:
<link rel="stylesheet" type="text/css" media="all" href="style.css"/>
it never gets loaded. No error, nothing, just resulting .pdf is missing style. If I put content of style file into template.HTML it works like charm.
If I put something like this:
<link rel="stylesheet" type="text/css" media="all" href="http://localhost:8080/MY_APP/resources/style.css"/>
it works.
All my resources are under src/main/webapp/resources.

After few more hours of researching problem, this is what I've end up with.
For CSS - only solution that I've found was to just put CSS in HTML templates. Not elegant, but does the job (at least for now). Not a big issue, as I use those templates to generate pdf files.
But the same issue was with image files, but that one I was able to solve in elegant way. Here is it!
Problem was that in web container Java couldn't find files pointed in .html templates.
So I had to write custom Element Factory extending ReplacedElementFactory. here is code for that:
public class B64ImgReplacedElementFactory implements ReplacedElementFactory {
public ReplacedElement createReplacedElement(LayoutContext c, BlockBox box, UserAgentCallback uac, int cssWidth, int cssHeight) {
Element e = box.getElement();
if (e == null) {
return null;
}
String nodeName = e.getNodeName();
if (nodeName.equals("img")) {
String attribute = e.getAttribute("src");
FSImage fsImage;
try {
fsImage = buildImage(attribute);
} catch (BadElementException e1) {
fsImage = null;
} catch (IOException e1) {
fsImage = null;
}
if (fsImage != null) {
if (cssWidth != -1 || cssHeight != -1) {
fsImage.scale(cssWidth, cssHeight);
}
return new ITextImageElement(fsImage);
}
}
return null;
}
protected FSImage buildImage(String srcAttr) throws IOException, BadElementException {
URL res = getClass().getClassLoader().getResource(srcAttr);
if (res != null) {
return new ITextFSImage(Image.getInstance(res));
} else {
return null;
}
}
public void remove(Element e) {
}
public void reset() {
}
#Override
public void setFormSubmissionListener(FormSubmissionListener listener) {
}
}
and use case in code generating PDF file:
ITextRenderer renderer = new ITextRenderer();
SharedContext sharedContext = renderer.getSharedContext();
sharedContext.setReplacedElementFactory(new B64ImgReplacedElementFactory());
custom element replaces catches all occurrence of 'img' nodes, and uses ClasLoader to get resource path, and based on that FSImage is returned, and that is what we need.
Hope that helps!

Related

Styling an external webpage with local css Xamarin forms

I'm getting an external page HTML code from my Backend as a string
and displaying it in a Webview in a Xamarin forms app
Now I would like to style it
I was wondering what is the most efficient way to do that?
and is it possible to style it in the same way a Xamarin Page would get styled with XAML and shared resources?
so far I tried referencing a CSS file in the shared resources, which I found out doesn't work...
htmlData = "<link rel=\"stylesheet\" type=\"text/css\"href=\"Assets\"Styles\"style.css\" />" + htmlData;
htmlSource.Html = htmlData;
myWebView.Source = htmlSource;
Update
I ended up using a custom renderer for the Webview
which worked for Android but not for IOS
here is my IOS implementation of the renderer
[assembly: ExportRenderer(typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespace XXX.iOS.Renderers
{
public class CustomWebViewRenderer : WkWebViewRenderer
{
WKUserContentController userController;
public CustomWebViewRenderer() : this(new WKWebViewConfiguration())
{
}
public CustomWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
userController = config.UserContentController;
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
var customWebView = e.NewElement as CustomWebView;
if (e.NewElement != null)
{
string htmldata = customWebView.HTMLData;
htmldata = "<link rel=\"stylesheet\" type=\"text/css\" href=\"StyleSheet.css\" />" + htmldata;
WkWebViewRenderer wkWebViewRenderer = new WkWebViewRenderer();
NSData data = NSData.FromString(htmldata);
wkWebViewRenderer.LoadData(data,"text/html", "UTF-8",new NSUrl(""));
}
}
}
}
Note: I don't have any idea what is happening here with the IOS code, cause I have never coded in the native language
I don't know whether this is feasible for you, but you could inject the actual CSS in the HTML string and then assign the HtmlSource
var css = ReadStringFromAsset("style.css");
htmlData = InjectCssInHtml(htmlData, css);
htmlSource.Html = htmlData;
myWebView.Source = htmlSource;
Depending on how much control you have over the HTML you receive, you have several option on how to realize InjectCssInHtml
Pseudo-Markup comment
If changing the HTML is feasible, you could add an HTML comment as a pdeudo markup. This will make the code simple, but each HTML must be edited accordingly
<html>
<head>
<style>
<!-- CSS -->
</style>
...
</html>
your InjectCssInHtml then becomes
string InjectCssInHtml(string html, string css)
{
return html.Replace("<!-- CSS -->", css);
}
Without editing the HTML
If editing the HTML is not feasible, the InjectCssInHtml becomes a tad more complicated. The following is a first guess, but I think you get the idea
string InjectCssInHtml(string html, string css)
{
string codeToInject;
int indexToInject = 0;
if(ContainsStyleTag(html))
{
indexToInject = IndexOfStyleTagContent(html);
codeToInject = css;
}
else if(ContainsHeadTag(html))
{
indexToInject = IndexOfHeadTagContents(html);
codeToInject = $"<style>{css}</style>";
}
else
{
indexToInject = IndexOfHtmlTagContents(html);
codeToInject = $"<head><style>{css}</style></head>";
}
return html.Insert(indexToInject, codeToInject);
}
Surely this does not cover each possible case, but I think you get the idea. The ìf-else` could be replaced by an abstract factory generational pattern combined with the strategy behavioral pattern.
string InjectCssInHtml(string html, string css)
{
ICssInjector injector = injectorFactory.CreateInjector(html);
return injector.InjectCss(html, css);
}

With OpenCSV, how do I append to existing CSV using a MappingStrategy?

With OpenCSV, how do I append to existing CSV using a MappingStrategy? There are lots of examples I could find where NOT using a Bean mapping stategy BUT I like the dynamic nature of the column mapping with bean strategy and would like to get it working this way. Here is my code, which just rewrites the single line to CSV file instead of appending.
How can I fix this? Using OpenCSV 4.5 . Note: I set my FileWriter for append=true . This scenario is not working as I expected. Re-running this method simply results in over-writing the entire file with a header and a single row.
public void addRowToCSV(PerfMetric rowData) {
File file = new File(PerfTestMetric.CSV_FILE_PATH);
try {
CSVWriter writer = new CSVWriter(new FileWriter(file, true));
CustomCSVMappingStrategy<PerfMetric> mappingStrategy
= new CustomCSVMappingStrategy<>();
mappingStrategy.setType(PerfMetric.class);
StatefulBeanToCsv<PerfMetric> beanToCsv
= new StatefulBeanToCsvBuilder<PerfMetric>(writer)
.withMappingStrategy(mappingStrategy)
.withSeparator(',')
.withApplyQuotesToAll(false)
.build();
try {
beanToCsv.write(rowData);
} catch (CsvDataTypeMismatchException e) {
e.printStackTrace();
} catch (CsvRequiredFieldEmptyException e) {
e.printStackTrace();
}
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Or, is the usual pattern to load all rows into a List and then re-write entire file? I was able to get it to work by writing two MappingStrategy mapping strategies and then conditionally using them with a if-file-exists but doing it that way leaves me with a "Unchecked assignment" warning in my code. Not ideal; hoping for an elegant solution?
I've updated OpenCSV to version 5.1 and got it working. In my case I needed the CSV headers to have a specific name and position, so I'm using both #CsvBindByName and #CsvBindByPosition, and needed to create a custom MappingStrategy to get it working.
Later, I needed to edit the MappingStrategy to enable appending, so when it's in Appending mode I don't need to generate a CSV header.
public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
private boolean useHeader=true;
public CustomMappingStrategy(){
}
public CustomMappingStrategy(boolean useHeader) {
this.useHeader = useHeader;
}
#Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
final int numColumns = FieldUtils.getAllFields(bean.getClass()).length;
super.setColumnMapping(new String[numColumns]);
if (numColumns == -1) {
return super.generateHeader(bean);
}
String[] header = new String[numColumns];
if(!useHeader){
return ArrayUtils.EMPTY_STRING_ARRAY;
}
BeanField<T, Integer> beanField;
for (int i = 0; i < numColumns; i++){
beanField = findField(i);
String columnHeaderName = extractHeaderName(beanField);
header[i] = columnHeaderName;
}
return header;
}
private String extractHeaderName(final BeanField<T, Integer> beanField){
if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0){
return StringUtils.EMPTY;
}
//return value of CsvBindByName annotation
final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
return bindByNameAnnotation.column();
}
}
Now if you use the default constructor it'll add the header to the generated CSV, and using a boolean you can tell it to add a header or to ignore it.
I never found an answer to this question and so what I ended up doing was doing a branch if-condition where .csv file exists or not. If file exists I used MappingStrategyWithoutHeader strategy, and if file didn't yet exist, I used MappingStrategyWithHeader strategy. Not ideal, but I got it working.

Disable RobotServer in crawler4j

I need to crawler a site to make some checks to know if the URLs are available or not periodically. For this, I am using crawler4j.
My problem comes with some web pages that have disabled the robots with <meta name="robots" content="noindex,nofollow" /> that make sense to not index this web pages in a search engine due to the content it have.
The crawler4j also is not following these links despite disable the configuration of the RobotServer. This must be very easy with robotstxtConfig.setEnabled(false);:
RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
robotstxtConfig.setUserAgentName(USER_AGENT_NAME);
robotstxtConfig.setEnabled(false);
RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher);
WebCrawlerController controller = new WebCrawlerController(config, pageFetcher, robotstxtServer);
...
But the described web pages are still not explored. I have read the code and this must be enough to disable the robots directives, but it is not working as expected. Maybe I am skipping something? I have tested it with versions 3.5 and 3.6-SNAPSHOT with identical result.
I am using a new version
<dependency>
<groupId>edu.uci.ics</groupId>
<artifactId>crawler4j</artifactId>
<version>4.1</version>
</dependency>`
After setting RobotstxtConfig like this, it is working:
RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
robotstxtConfig.setEnabled(false);
Testing result and Source Code in from Crawler4J proves that:
public boolean allows(WebURL webURL) {
if (config.isEnabled()) {
try {
URL url = new URL(webURL.getURL());
String host = getHost(url);
String path = url.getPath();
HostDirectives directives = host2directivesCache.get(host);
if ((directives != null) && directives.needsRefetch()) {
synchronized (host2directivesCache) {
host2directivesCache.remove(host);
directives = null;
}
}
if (directives == null) {
directives = fetchDirectives(url);
}
return directives.allows(path);
} catch (MalformedURLException e) {
logger.error("Bad URL in Robots.txt: " + webURL.getURL(), e);
}
}
return true;
}
When set Enabled as false, it will not do the check any more.
Why don't you just exclude everything about Robotstxt in crawler4j? I needed to crawl a site and ignore the robots and this worked for me.
I changed CrawlController and WebCrawler in .crawler like that:
WebCrawler.java:
delete
private RobotstxtServer robotstxtServer;
delete
this.robotstxtServer = crawlController.getRobotstxtServer();
edit
if ((shouldVisit(webURL)) && (this.robotstxtServer.allows(webURL)))
-->
if ((shouldVisit(webURL)))
edit
if (((maxCrawlDepth == -1) || (curURL.getDepth() < maxCrawlDepth)) &&
(shouldVisit(webURL)) && (this.robotstxtServer.allows(webURL)))
-->
if (((maxCrawlDepth == -1) || (curURL.getDepth() < maxCrawlDepth)) &&
(shouldVisit(webURL)))
CrawlController.java:
delete
import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer;
delete
protected RobotstxtServer robotstxtServer;
edit
public CrawlController(CrawlConfig config, PageFetcher pageFetcher, RobotstxtServer robotstxtServer) throws Exception
-->
public CrawlController(CrawlConfig config, PageFetcher pageFetcher) throws Exception
delete
this.robotstxtServer = robotstxtServer;
edit
if (!this.robotstxtServer.allows(webUrl))
{
logger.info("Robots.txt does not allow this seed: " + pageUrl);
}
else
{
this.frontier.schedule(webUrl);
}
-->
this.frontier.schedule(webUrl);
delete
public RobotstxtServer getRobotstxtServer()
{
return this.robotstxtServer;
}
public void setRobotstxtServer(RobotstxtServer robotstxtServer)
{
this.robotstxtServer = robotstxtServer;
}
Hope that it's what you're looking for.

Flex navigateToURL from iframe POST

Let me explain my current issue right now:
I have a webapp located at domain A. Let's call it A-App. I open an iframe from A-App that points to a Flex app on domain B. We'll call it B-FlexApp. B-FlexApp wants to post some data to another app located on the same domain, we'll call it B-App. The problem is that in IE the communication breaks somewhere between B-FlexApp and B-App while B-FlexApp is opened in the iframe. This only happens in IE.
However when opening B-FlexApp in a new window, posting the data to B-App works just fine. How to overcome this? Dropping the iframe is not possible.
There´s a issue with AS3 navigateToURL and IE. You can try calling javascript to navigate: I have a little utility class to handle this:
//class URLUtil
package com
{
import flash.external.*;
import flash.net.*;
public class URLUtil extends Object
{
protected static const WINDOW_OPEN_FUNCTION:String="window.open";
public function URLUtil()
{
super();
return;
}
public static function openWindow(arg1:String = "", arg2:String="_blank", arg3:String=""):void
{
var browserName:String = getBrowserName();
switch (browserName)
{
case "Firefox":
{
flash.external.ExternalInterface.call(WINDOW_OPEN_FUNCTION, arg1, arg2, arg3);
break;
}
case "IE":
{
flash.external.ExternalInterface.call("function setWMWindow() {window.open(\'" + arg1 + "\');}");
break;
}
case "Safari":
case "Opera":
{
flash.net.navigateToURL(new URLRequest(arg1), arg2);
break;
}
default:
{
flash.net.navigateToURL(new URLRequest(arg1), arg2);
break;
}
}
return;
}
private static function getBrowserName():String
{
var str:String="";
var browserName:String = ExternalInterface.call("function getBrowser(){return navigator.userAgent;}");
if (!(browserName == null) && browserName.indexOf("Firefox") >= 0)
{
str = "Firefox";
}
else
{
if (!(browserName == null) && browserName.indexOf("Safari") >= 0)
{
str = "Safari";
}
else
{
if (!(browserName == null) && browserName.indexOf("MSIE") >= 0)
{
str = "IE";
}
else
{
if (!(browserName == null) && browserName.indexOf("Opera") >= 0)
{
str = "Opera";
}
else
{
str = "Undefined";
}
}
}
}
trace("Browser: \t" + str);
return str;
}
}
}
and you call it like:
btn.addEventListener(MouseEvent.CLICK, onBTNClick);
function onBTNClick(evt:MouseEvent):void
{
URLUtil.openWindow(YOUR_URL_STRING);
}
Hope it helps!
It is better to let the browser actually does the "navigate to URL" function instead of Flex.
For example, in the page that contains the Flex app, the page would contain a Javascript function call handleNavigationRequest(pageName, target). In the Flex application, you may utilize ExternalInterface, and call the handleNavigationRequest.
By using this paradigm, the Flex application would not have to figure the details as to how the external implementations such as frame setup, etc, and you end up having a cleaner and less-coupled design.
I've found out that i can use swfObject to embed the flash object thus the iframe implementation is completely useless. Embedding the flash component in the overlay, instead of opening it in an iframe, makes IE behave properly.
I had the same problem and I solved it simply passing the second argument (browser window) to the function:
navigateToUrl(url,"_blank"); , in my case I use "_blank".
It works with IE8 and IE9.
Davide

Display an ashx image using jQuery?

I've been trying to use the jQuery plugin Colorbox to display images I have in my DB through an ashx file. Unfortunately it just spits a bunch of gibberish at the top of the page and no image. Can this be done? Here is what I have so far:
$(document).ready
(
function ()
{
$("a[rel='cbImg']").colorbox();
}
);
...
<a rel="cbImg" href="HuntImage.ashx?id=15">Click to see image</a>
UPDATE:
My ashx file is writing the binary out:
context.Response.ContentType = "image/bmp";
context.Response.BinaryWrite(ba);
Colorbox has an option 'photo'. If you set this to true in your constructor then it will force it to render the photo.
$(target).colorbox({photo: true});
You should be setting the src attribute in the client side.
<img src="HuntImage.ashx?id=15" ..../>
The handler
public class ImageRequestHandler: IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
context.Response.Clear();
if(context.Request.QueryString.Count != 0)
{
//Get the stored image and write in the response.
var storedImage = context.Session[_Default.STORED_IMAGE] as byte[];
if (storedImage != null)
{
Image image = GetImage(storedImage);
if (image != null)
{
context.Response.ContentType = "image/jpeg";
image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
}
}
}
private Image GetImage(byte[] storedImage)
{
var stream = new MemoryStream(storedImage);
return Image.FromStream(stream);
}
public bool IsReusable
{
get { return false; }
}
}
It appears that I can't do what I am trying using colorbox with an ashx image. If anyone finds a way please post it here.
I considered deleting the question but I will leave it up incase someone else runs into the same issue.
Find this function around line 124 (colorbox 1.3.15)
// Checks an href to see if it is a photo.
// There is a force photo option (photo: true) for hrefs that cannot be matched by this regex.
function isImage(url) {
return settings.photo || /\.(gif|png|jpg|jpeg|bmp)(?:\?([^#]*))?(?:#(\.*))?$/i.test(url);
}
On line 127, add |ashx after bmp in (gif|png|jpg|jpeg|bmp) so it reads like this:
// Checks an href to see if it is a photo.
// There is a force photo option (photo: true) for hrefs that cannot be matched by this regex.
function isImage(url) {
return settings.photo || /\.(gif|png|jpg|jpeg|bmp|ashx)(?:\?([^#]*))?(?:#(\.*))?$/i.test(url);
}
This is working just fine for me in Sitecore 6.2 :)

Resources