I am trying to generate a pdf document from a Java String containing html
code. I am using "Freemarker" as a templating engine to generate the html
content and then "Flying-Saucer" to convert this generated html to pdf.
My problem is that images aren't rendered in the produced pdf. The exact
details about how I am generating are as follows:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.lowagie.text.DocumentException;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.SimpleHash;
import freemarker.template.SimpleSequence;
import freemarker.template.Template;
import freemarker.template.TemplateException;
#Singleton
public class FlyingSaucerTaxInvoicePdfPrinter implements ITaxInvoicePdfPrinter {
private final Configuration m_cfg;
#Inject
public FlyingSaucerTaxInvoicePdfPrinter() {
// TODO: Following should be singletons and injected
m_cfg = new Configuration();
m_cfg.setObjectWrapper(new DefaultObjectWrapper());
m_cfg.setClassForTemplateLoading(this.getClass(), "/");
}
private Template getTemplate() throws IOException {
return m_cfg.getTemplate(PdfResources.TAX_INVOICE_TEMPLATE);
}
#Override
public void printToPdf(TaxInvoiceUiPb taxInvoice, OutputStream pdfOutputStream) {
OutputStream htmlOuputStream = null;
try {
htmlOuputStream = new ByteArrayOutputStream();
printHtml(htmlOuputStream, taxInvoice);
generatePDF(htmlOuputStream, pdfOutputStream);
} catch (Exception e) {
throw new LoggedRuntimeException(e);
} finally {
try {
htmlOuputStream.close();
} catch (IOException e) {
throw new LoggedRuntimeException(e);
}
}
}
private void generatePDF(OutputStream htmlOuputStream, OutputStream pdfOutputStream)
throws DocumentException, IOException {
try {
ITextRenderer renderer = new ITextRenderer(30.666f, 20);
String html = htmlOuputStream.toString();
logHtml(html);
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(pdfOutputStream);
} finally {
pdfOutputStream.close();
}
}
// Some methods not shown as irrelevant
}
The generated html (showing only relevant section) is:
<body>
<div class="main" background="images/invoice-bg.jpg">
<img src="images/invoice-bg.jpg"></img>
<div class="header">
<div class="logo"><img src="images/invoice-logo.jpg" alt="" border="0" /></div>
<div class="heading">booking invoice</div>
</div>
<div class="clear"></div>
</div>
</body>
This code runs as a War deployed on Tomcat. The location of the images in the War as output of
tree command (run inside WEB-INF) is:
|-- classes
| |-- com
| | `-- ilodge
| | `-- pmsServerWar
| | |-- PmsServerWarListener.class
| | `-- PmsServerWarServletModule.class
| |-- images
| | |-- invoice-bg.jpg
| | |-- rupees-icon-total.png
| | |-- thank-you.jpg
| | |-- total-bold-rupee.png
| | `-- ul-bor.jpg
| |-- taxInvoice.css
| |-- taxInvoiceFooter.ftl
| |-- taxInvoice.ftl
| `-- test.ftl
|-- lib
| |-- addressServer-1.0-SNAPSHOT.jar
| |-- addressUiProtobuf-1.0-SNAPSHOT.jar
| `-- xml-apis-1.3.03.jar
`-- web.xml
I have truncated the output for brevity. Please help.
Thanks and regards,
Rohit
Try setting the baseUrl parameter.
I had the same problem - I'm passing in html and wasn't getting images (or css for that matter) in the pdf. I used the exact same thing as you:
renderer.setDocumentFromString(html);
That method can also take a base url parameter:
renderer.setDocumentFromString(content, baseUrl)
Where baseUrl = the root folder (in my case it's a web application, so it was 'http ://server:port/app'). It seems to work like a base href - relative paths build on the baseUrl. Once I added that in, blammo - images and css.
A possible problem can be that you use relative URL's in the html code. Try to use an absolute URL, instead of relative.
Related
I have a small, browser-based game that I'm trying to get Jest up and running with.
My goal is to be able to write tests, and to have them run with Jest, and not to have any extra DOM- or browser API-related error messages.
As the game makes use of DOM and canvas, I need a solution where I can either mock those manually, or have Jest take care of it for me. At a minimum, I'd like to verify that the 'data model' and my logic is sane.
I'm also making use of ES6 modules.
Here's what I've tried so far:
Tried running jest:
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html
Details:
/home/dingo/code/game-sscce/game.spec.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import { Game } from './game';
^^^^^^
SyntaxError: Cannot use import statement outside a module
I understood here that I can experimentally enable ES module support, or use a transpiler to output ES5 that Jest can recognize and run.
So my options are:
Enable experimental ES module support
Transpile using Babel
Transpile using Parcel
Transpile using Webpack
I decided to try Babel and looked here for instructions: https://jestjs.io/docs/en/getting-started#using-babel
I created a babel.config.js file in the root directory.
After installing babel and creating a config file, here's an SSCCE:
babel.config.js
module.exports = {
presets: [
[
'#babel/preset-env'
]
],
};
game.js
export class Game {
constructor() {
document.getElementById('gameCanvas').width = 600;
}
}
new Game();
game.spec.js
import { Game } from './game';
test('instantiates Game', () => {
expect(new Game()).toBeDefined();
});
index.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<script type="module" src="game.js" defer></script>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas" />
</div>
</body>
</html>
package.json
{
"name": "game-sscce",
"version": "1.0.0",
"scripts": {
"test": "jest"
},
"devDependencies": {
"#babel/core": "^7.12.13",
"#babel/preset-env": "^7.12.13",
"babel-jest": "^26.6.3",
"jest": "^26.6.3"
}
}
Now when I try running Jest again, I get:
FAIL ./game.spec.js
● Test suite failed to run
TypeError: Cannot set property 'width' of null
1 | export class Game {
2 | constructor() {
> 3 | document.getElementById('gameCanvas').width = 600;
| ^
4 | }
5 | }
6 |
at new Game (game.js:3:5)
at Object.<anonymous> (game.js:7:1)
at Object.<anonymous> (game.spec.js:1:1)
...and now, I'm not sure what to do. If document is not being recognized, then I suspect Jest is not making use of jsdom properly. Am I supposed to configure anything else?
Investigation:
Jest runs with jsdom by default.
document actually exists:
However, since it's mocked, getElementById() just returns null.
In this situation, it's not possible to return an existing canvas defined in the HTML document. Rather, one can create the canvas programmatically:
game.js
export class Game {
constructor() {
const canvas = document.createElement('canvas');
canvas.setAttribute('id', 'gameCanvas');
document.getElementById('gameContainer').append(canvas);
canvas.width = 600;
}
}
new Game();
getElementById() will, however, still return null, so this call must be mocked:
game.spec.js
import { Game } from './game';
test('instantiates Game', () => {
jest.spyOn(document, 'getElementById').mockReturnValue({})
expect(new Game()).toBeDefined();
});
The test still fails to run:
FAIL ./game.spec.js
● Test suite failed to run
TypeError: Cannot read property 'append' of null
3 | const canvas = document.createElement('canvas');
4 | canvas.setAttribute('id', 'gameCanvas');
> 5 | document.getElementById('gameContainer').append(canvas);
| ^
6 |
7 | canvas.width = 600;
8 |
at new Game (game.js:5:5)
at Object.<anonymous> (game.js:16:1)
at Object.<anonymous> (game.spec.js:1:1)
This is because Game is instantiating itself as soon as Jest imports it due to the new Game() call on the last line. Once rid of that:
game.js
export class Game {
constructor() {
const canvas = document.createElement('canvas');
canvas.setAttribute('id', 'gameCanvas');
document.getElementById('gameContainer').append(canvas);
canvas.width = 600;
}
}
We get:
FAIL ./game.spec.js
✕ instantiates Game (7 ms)
● instantiates Game
TypeError: document.getElementById(...).append is not a function
3 | const canvas = document.createElement('canvas');
4 | canvas.setAttribute('id', 'gameCanvas');
> 5 | document.getElementById('gameContainer').append(canvas);
| ^
6 |
7 | canvas.width = 600;
8 |
at new Game (game.js:5:46)
at Object.<anonymous> (game.spec.js:5:10)
One step closer, but the append() call must also be mocked out:
game.spec.js
import { Game } from './game';
test('instantiates Game', () => {
jest.spyOn(document, 'getElementById').mockReturnValue({
append: jest.fn().mockReturnValue({})
});
expect(new Game()).toBeDefined();
});
...and now the test passes:
PASS ./game.spec.js
✓ instantiates Game (9 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
It's interesting that jsdom returns an HTMLCanvasElement when created programmatically and mocked:
However, it can't really be used for anything:
game.js
export class Game {
constructor() {
const canvas = document.createElement('canvas');
canvas.setAttribute('id', 'gameCanvas');
document.getElementById('gameContainer').append(canvas);
canvas.width = 600;
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(200, 0, 0)';
ctx.fillRect(10, 10, 50, 50);
ctx.fillStyle = 'rgba(0, 0, 200, 0.5)';
ctx.fillRect(30, 30, 50, 50);
}
}
As shown by the failing test:
FAIL ./game.spec.js
✕ instantiates Game (43 ms)
● instantiates Game
TypeError: Cannot set property 'fillStyle' of null
10 | var ctx = canvas.getContext('2d');
11 |
> 12 | ctx.fillStyle = 'rgb(200, 0, 0)';
| ^
13 | ctx.fillRect(10, 10, 50, 50);
14 |
15 | ctx.fillStyle = 'rgba(0, 0, 200, 0.5)';
at new Game (game.js:12:5)
at Object.<anonymous> (game.spec.js:7:10)
console.error
Error: Not implemented: HTMLCanvasElement.prototype.getContext (without installing the canvas npm package)
at module.exports (/home/dingo/code/game-sscce/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
at HTMLCanvasElementImpl.getContext (/home/dingo/code/game-sscce/node_modules/jsdom/lib/jsdom/living/nodes/HTMLCanvasElement-impl.js:42:5)
at HTMLCanvasElement.getContext (/home/dingo/code/game-sscce/node_modules/jsdom/lib/jsdom/living/generated/HTMLCanvasElement.js:130:58)
at new Game (/home/dingo/code/game-sscce/game.js:10:22)
at Object.<anonymous> (/home/dingo/code/game-sscce/game.spec.js:7:10)
at Object.asyncJestTest (/home/dingo/code/game-sscce/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
at /home/dingo/code/game-sscce/node_modules/jest-jasmine2/build/queueRunner.js:45:12
at new Promise (<anonymous>)
at mapper (/home/dingo/code/game-sscce/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
at /home/dingo/code/game-sscce/node_modules/jest-jasmine2/build/queueRunner.js:75:41 undefined
8 | canvas.width = 600;
9 |
> 10 | var ctx = canvas.getContext('2d');
| ^
11 |
12 | ctx.fillStyle = 'rgb(200, 0, 0)';
13 | ctx.fillRect(10, 10, 50, 50);
To be able to test further, either of the following two conditions must be fulfilled:
canvas has to be installed as a peer dependency of jsdom,
jest-canvas-mock has to be installed.
I try to navigate from list page to detail page, when i tried with the below code. I got error stating that field error. For that I've tried with adding a empty Slug field in models, it shows an page not found error.
#urls.py
from django.urls import path
from .views import (TaskListView,TaskDetailView)
app_name = 'Tasks'
urlpatterns = [
path('', TaskListView.as_view(), name='list'),
path('<slug:slug>/', TaskDetailView.as_view(), name='detail'),
]
#views.py
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
from django.views.generic import ListView, DetailView, View
from .models import Taskmanager
def home(request):
return render(request, 'home.html')
class TaskListView(ListView):
template_name = 'Tasks.html'
model = Taskmanager
context_object_name = 'data'
class TaskDetailView(DetailView):
template_name = 'detail.html'
model = Taskmanager
context_object_name = 'data'
#models.py
from django.db import models
from django.urls import reverse
# Create your models here.
week_number = (("week01", "week01"),
("week02", "week02"),
("week03", "week03"),
("week04", "week04"),
("week05", "week05"),
("week06", "week06"),
("week07", "week07"),
("week08", "week08"),
("week09", "week09"),
("week10", "week10"),
("week11", "week11"),
("week12", "week12"),
("week13", "week13"),
("week14", "week14"),
("week15", "week15"),
("week16", "week16"),
("week17", "week17"),
("week18", "week18"),
("week19", "week19"),
("week20", "week20"),
("week21", "week21"),
("week22", "week22"),
("week23", "week23"),
("week24", "week24"),
("week25", "week25"),
("week26", "week26"),
("week27", "week27"),
("week28", "week28"),
("week29", "week29"),
("week30", "week30"),
("week31", "week31"),
("week32", "week32"),
("week33", "week33"),
("week34", "week34"),
("week35", "week35"),
("week36", "week36"),
("week37", "week37"),
("week38", "week38"),
("week39", "week39"),
("week40", "week40"),
("week41", "week41"),
("week42", "week42"),
("week43", "week43"),
("week44", "week44"),
("week45", "week45"),
("week46", "week46"),
("week47", "week47"),
("week48", "week48"),
("week49", "week49"),
("week50", "week50"),
("week51", "week51"),
("week52", "week52"),
("week53", "week53"),
)
class Taskmanager(models.Model):
CurrentSprint = models.CharField(max_length=10, default="week01",
choices=week_number)
todaydate = models.DateField()
taskname = models.SlugField(max_length=200)
testrun = models.URLField(max_length=300)
comments = models.CharField(max_length=300)
assignedto = models.EmailField(max_length=70)
def __str__(self):
return self.taskname
def get_absolute_url(self):
return reverse('Tasks:detail', kwargs={'slug': self.taskname})
#Tasks.html
<a href="{% url 'Tasks:detail' slug='detail'%}"> {{Taskmanager.todaydate}}
</a>
I need an output when I click the link, it needs to navigate to the details page where the details of the task needs to be displayed.
try adding this
#views.py
class TaskDetailView(DetailView):
...
def get_object(self):
instance = get_object_or_404(Taskmanager, slug=self.kwargs['slug'])
return instance
#models.py
django.db.models.signals import pre_save
class Taskmanager(models.Model):
...
taskname = models.CharField(max_length=200)
slug = models.SlugField(max_length=100)
...
def pre_save_Taskmanager_receiver(instance, *args, **kwargs):
if not instance.slug:
instance.slug = instance.taskname
pre_save.connect(pre_save_Taskmanager_receiver, sender= Taskmanager)
# task.html
{{ data.todaydate }}
I am building simple chatting application using WebSocket using Eclipse and apache 7.x .However I am not able to compile my code cause it shows import javax.websocket cannot be resolved. After Googling a lot i found Link that shows external jar in application. I added a jar in my library section. It removed my current error but gave another. Now it showing WebSocket connection to 'ws://localhost:8080/WebSocket/socket' failed: Error during WebSocket handshake: Unexpected response code: 404
I'v tried a lot like adding servlet-api.jar to build path from apache lib. And also find some information like It provides also the javax.websocket-api-1.0.jar library so this is not needed in you application. You might need it at compile time but the server will provide it at runtime
Now I got stuck like in recursion. when I remove my jar got first error and when I add external jar then got second error. please help me in this issue cause we are developing mini project for real time collaborative editor and we are suppose to use websocket in it.
Now here is my code for websocket(not using maven):
WebSocket.java
package com.psl.service;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONWriter;
#ServerEndpoint("/socket")
public class WebSocket {
Set<Session> sessionSet = Collections.synchronizedSet(new HashSet<>());
#OnOpen
public void onOpen(Session webSession) {
System.out.println("Opened");
sessionSet.add(webSession);
}
#OnMessage
public void onMessage(String message, Session webSession) throws IOException {
System.out.println("Got message "+message);
String username = (String)webSession.getUserProperties().get("username");
if(username==null) {
webSession.getUserProperties().put("username", message);
webSession.getBasicRemote().sendText(buildJSON(username, message));
}
else {
for(Session s : sessionSet) {
s.getBasicRemote().sendText(buildJSON(username, message));
}
}
}
private String buildJSON(String string, String string2) {
String jsonData="";
JSONObject obj=new JSONObject();
try {
obj.put("message",string + ":" + string2);
StringWriter out = new StringWriter();
JSONWriter jwriter = new JSONWriter(out);
jwriter.key(string).value(string2);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#OnClose
public void onClose() {
System.out.println("CLosed");
sessionSet.remove(sessionSet);
}
}
chat.html
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
<script type="text/javascript">
var websocket = new WebSocket("ws://localhost:8080/WebSocketDemo/socket");
websocket.onmessage = function onMessage(message) {
var jsonData = JSON.parse(message.data);
if(jsonData.message != null) {
messageTextArea.value += jsonData.message + "\n";
}
}
function sendMessage() {
websocket.send(sendText.value);
sendText.value = "";
}
</script>
</head>
<body>
<textarea rows="30" cols="70" id="messageTextArea" ></textarea><br /><br />
<input id="sendText">
<input type="button" value="Send" onclick="sendMessage()">
</body>
</html>
I got answer for this query. This is because the added jar overrides my internal jars in apache tomcat. And I am using older tomcat that have not websocket-api.jar
So My solution for this problem is either use glashfish 4.x or else use apache tomcat 8.x that gives us required jar to run websocket. No need to add any extra websocket api jar.
I have the following models
class NewSlide(models.Model):
slider = models.ForeignKey('NewSliderPlugin')
title = models.CharField(max_length=255)
content = models.TextField(max_length=80, null=True)
link = models.CharField(max_length=255)
image = models.ImageField(upload_to='slides', null=True)
visible = models.BooleanField(default=False)
def __unicode__(self): # Python 3: def __str__(self):
return self.title
class NewSliderPlugin(CMSPlugin):
title = models.CharField(max_length=255)
template = models.CharField(max_length=255, choices=(('slider.html','Top Level Slider'), ('slider2.html','Featured Slider')))
The plugin code as below:
class NewSlideInline(admin.StackedInline):
model = NewSlide
extra = 1
class NewCMSSliderPlugin(CMSPluginBase):
model = NewSliderPlugin
name = "NewSlider"
render_template = "slider.html"
inlines = [NewSlideInline]
def render(self, context, instance, placeholder):
self.render_template = instance.template
print instance.title
print instance.newslide_set.all(), 1111111111111111
context.update({
'slider': instance,
'object': instance,
'placeholder': placeholder
})
return context
I have added slides to the plugin and published changes, however 1instance.newslide_set.all()1 returns empty list: [] 1111111111111111
Update:
it creates 2 records, somehow the admin references 49, but render code gives 63
mysql> select * from cmsplugin_newsliderplugin;
+------------------+-----------+-------------+
| cmsplugin_ptr_id | title | template |
+------------------+-----------+-------------+
| 49 | slide | slider.html |
| 63 | slide | slider.html |
+------------------+-----------+-------------+
mysql> select * from slider_newslide;
+----+-----------+-------+---------+------+----------------+---------+
| id | slider_id | title | content | link | image | visible |
+----+-----------+-------+---------+------+----------------+---------+
| 6 | 49 | ttttt | testt | test | slides/287.jpg | 0 |
+----+-----------+-------+---------+------+----------------+---------+
By the way, I have django-reversion installed, not sure if it's because of this app.
OK according to the documentation I need to copy the related items:
class NewSliderPlugin(CMSPlugin):
title = models.CharField(max_length=255)
template = models.CharField(max_length=255, choices=(('slider.html','Top Level Slider'), ('slider2.html','Featured Slider')))
def copy_relations(self, oldinstance):
for slide in oldinstance.newslide_set.all():
# instance.pk = None; instance.pk.save() is the slightly odd but
# standard Django way of copying a saved model instance
slide.pk = None
slide.slider = self
slide.save()
my target is to analyze an "html - String". In the end i'd like
to extract the Textnodes and datanodes of a string and store them in
different lists.
With my first appoach I tried to go through a "html - String" recursively.
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import java.util.Iterator
import org.jsoup.nodes
import org.jsoup.nodes.Node
object TextAnalyzer {
def processNode(node: Node) {
if (node.isInstanceOf[TextNode]) println(node.toString())
node.childNodes() foreach processNode
}
def main(args: Array[String]) {
val myHtml = "<html> <head> <title>Welcome</title> </head> <body> <div> <p>Foo</p> </div> </body></html>";
val doc = Jsoup.parse(myHtml);
processNode(doc);
}
}
It ends with the following errow message:
scalac MyModule.scala
MyModule.scala:23: error: value childs is not a member of org.jsoup.nodes.Node
node.childNodes() foreach processNode
^
one error found
>
Can you get me startet in order to be able to
get the data- and textnodes of a textstring ?
... recursively ?
Thanks in advance for help?
greets
Ansgar
I don't really understand your question - but the following compiles. Is it what you were aiming for?
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import java.util.Iterator
import org.jsoup.nodes._
import scala.collection.JavaConversions._
object TextAnalyzer extends App {
def processNode(node: Node) {
if (node.isInstanceOf[TextNode]) println(node.toString())
node.childNodes() foreach processNode
}
val myHtml = "<html> <head> <title>Welcome</title> </head> <body> <div> <p>Foo</p> </div> </body></html>";
val doc = Jsoup.parse(myHtml);
processNode(doc);
}