How to set QTextDocument margins and other properties (setHTML, print to pdf)? - html

I have the following certificate class for producing pdf document out of some images and data. After setting image sources, I call generate() function and get test.pdf output file. The document is created based on QTextDocument class using setHtml(html) method.
The problem is that I have huge white spaces around the document, while I want the title 'REPORT' with logo image to be on the very top of the page. I would also like to add lower border to the table, but as I understand it is not supported by Qt (Supported HTML Subset).
Python3 code:
class certificate:
def __init__(self):
self.logo = None
pdffile = 'test.pdf'
self.histogram = None
self.printer = QPrinter()
self.printer.setPageSize(QPrinter.Letter)
self.printer.setOutputFormat(QPrinter.PdfFormat)
self.printer.setOutputFileName(pdffile)
def generate(self):
document = QTextDocument()
html = ""
html += ('<head><title>Report</title><style></style></head>'
'<body><table width="100%"><tr>'
'<td><img src="{}" width="30"></td>'
'<td><h1>REPORT</h1></td>'
'</tr></table>'
'<p align=right><img src="{}" width="300"></p>'
'<p align=right>Sample</p></body>').format(self.logo, self.histogram)
document.setHtml(html)
document.print_(self.printer)
I never extensively used html before and never worked with QTextDocument, and would appreciate any advice on how to control document margins and table properties.
Other related property I want to control is resolution - I use pixel image size and need to know page and margin sizes in pixels.
EDITED: The question is almost answered by #mata. I can set now any margins and resolution, but do not understand how to control image and font sizes. E.g. if I need that an image is always 50mm wide, and html header and main text font sizes are visually the same - how to implement it?
EDITED2: The last part is solved too. Here is modified code by #mata, it gives the same result for any dpi value:
dpi=96
document = QTextDocument()
html = """
<head>
<title>Report</title>
<style>
</style>
</head>
<body>
<table width="100%">
<tr>
<td><img src="{0}" width="{1}"></td>
<td><h1>REPORT</h1></td>
</tr>
</table>
<hr>
<p align=right><img src="{2}" width="{3}"></p>
<p align=right>Sample</p>
</body>
""".format('D:\Documents\IST Projects\diashape\docbook\Installation\images\istlogo_medium.png',
40*dpi/96,
'D:\Documents\IST Projects\diashape\docbook\Installation\images\istlogo_medium.png',
200*dpi/96)
document.setHtml(html)
printer = QPrinter()
font = QFont()
font.setPointSize(12*dpi/96)
document.setDefaultFont(font)
printer.setResolution(dpi)
...

You can specify what resolution you want to use in the constructor when you create the QPrinter.
Then after you've set the pagesize, you can use width, height and resolution on the printer to fint out that values, here's what I got for Letter (dpi-values can be different, they depend on the screen or the printer):
QPrinter(QPrinter.ScreenResolution) # 96dpi, 752x992
QPrinter(QPrinter.PrinterResolution) # 72dpi, 564x744
QPrinter(QPrinter.HighResolution) # 1200dpi, 9400x12400
You can also set the dpi directly using setResolution.
The size returned by width and height is the page size (same as pageRect().size()), which ist not the same as the paper size - as the page also has margins, which you can set like this:
printer.setPageMargins(12, 16, 12, 20, QPrinter.Millimeter)
this sets left and right margins to 12mm, top to 16mm and bottom to 20mm - just for example, if you want less white space you can obviously just use smaller values.
And you should set the document size to the size of the resulting size:
document.setPageSize(QSizeF(printer.pageRect().size()))
as you've noticed yourself, the subset of html and css allowed is very limited, specially for formatting tables. But instead of using a lower border on the table you could just use a hr, which probably will look like you want it.
At least it doesn't look that bad if I test it like this:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
a=QApplication([])
document = QTextDocument()
html = """
<head>
<title>Report</title>
<style>
</style>
</head>
<body>
<table width="100%">
<tr>
<td><img src="{}" width="30"></td>
<td><h1>REPORT</h1></td>
</tr>
</table>
<hr>
<p align=right><img src="{}" width="300"></p>
<p align=right>Sample</p>
</body>
""".format('', '')
document.setHtml(html)
printer = QPrinter()
printer.setResolution(96)
printer.setPageSize(QPrinter.Letter)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName("test.pdf")
printer.setPageMargins(12, 16, 12, 20, QPrinter.Millimeter)
document.setPageSize(QSizeF(printer.pageRect().size()))
print(document.pageSize(), printer.resolution(), printer.pageRect())
document.print_(printer)

Related

Html table won't print full width

Help, trying to print a calendar using html table, prints fine on mac but windows, all browsers, it's putting a 3" margin at top no matter what the CSS print settings. Client wants calendar to print full bleed. I can get it to print full size if I adjust the print settings on the browser to not be shrink to fit and then set it to 110% but that solution screws up the type and messes with the characters in the calendar. Is there any way to do it using straight CSS?
Add this simple Javascript code to print the calendar
<script language="javascript">
function Clickheretoprint()
{
var disp_setting="toolbar=yes,location=no,directories=yes,menubar=yes,";
disp_setting+="scrollbars=yes,width=400, height=400, left=100, top=25";
var content_vlue = document.getElementById("print_content").innerHTML;
var docprint=window.open("","",disp_setting);
docprint.document.open();
docprint.document.write('<html><head><title>Print</title>');
docprint.document.write('</head><body onLoad="self.print()" style="width: 800px; font-size:12px; font-family:arial;">');
docprint.document.write(content_vlue);
docprint.document.write('</body></html>');
docprint.document.close();
docprint.focus();
}
</script>
Print
<div id="print_content" style="width: 100%;">
Calendar/html table goes here
</div>

debug nasty horizontal scroll

I think i got myself entangled in a CSS maze. I notice a horizontal scroll on my site in desktop browsers (firefox and chromium), when in responsive mode. Tested in android, and it seems ok.
The website is cv.pixyz.net
To debug it, I tried all of the following:
Looking for elements getting bigger than the parent's space.
I thought the container with #id was the problem, because web developer toolbar shows that closer to the edges of the screen, but removing that, didn't solve this
Used this to see if anything gets out of bounds. some elements stand out, but still can't solve the scroll
I tried these 2 snippets:
// snippet 1
var docWidth = document.documentElement.offsetWidth;
[].forEach.call(
document.querySelector('body *'),
function(el) {
console.log(el);
// console.log(el.offsetWidth);
// console.log(docWidth);
if (el.offsetWidth > docWidth) {
console.log(el);
}
}
);
// snippet 2
var all = document.getElementsByTagName("*"), i = 0, rect;
for (; i < all.length; i++) {
rect = all[i].getBoundingClientRect();
if (rect.right < 0) all[i].style.outline = "1px solid green";
}
but there's no effect either: no logs registered, no border changed
started removing other elements in the page. Even doing this, I still get scroll:
<!DOCTYPE html>
<!-- domActual = <?php echo $ambiente; ?> -->
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Sobre mim... # Luis Aguiar</title>
</head>
<body>
<nav class="container">
<h2 class="nome">Sobre mim... / Luis Aguiar</h2>
<a class="dominio" href="http://www.cv.pixyz.net">cv.pixyz.net</a>
<ul>
<li>
ID
</li>
<li>
Dev
</li>
</ul>
</nav>
<footer>
<p>Todos os direitos reservados # Luis Aguiar</p>
</footer>
</body>
</html>
I also tried this to check abnormal widths: (http://wernull.com/2013/04/debug-ghost-css-elements-causing-unwanted-scrolling/):
* {
outline: 1px solid blue!important;
opacity: 1 !important;
visibility: visible !important;
}
Does anyone know what is causing this, or have any other idea for debugging?
The problem appears to be the following line :
<section id="dev">
[...]
<li class="job"> /* 2nd li element */
[...]
<p class="url">https://www.demarca.eu/</p> /* <- This line */
The URL has no breaking spaces, so once the window reaches the width of the URL string it can't wrap the string and therefore the scrollbar gets added.
The options you have are:
Shorten the text:
Consider whether you need to display the full URL including https:// - maybe instead include it as a link? e.g.:
<p class="url">www.demarca.eu</p>
Use lowercase: the CSS changes the text to uppercase, which adds to the width of the string.
Wrap the URL: forcing the string to wrap is often the best option, but it doesn't suit a url so well because urls can't have spaces. However if you do want to make it wrap, you can create the following CSS class and add it to the element:
.wrap { word-wrap: break-word; }
I don't really know what it was, but after reboot, was ok (... but i cleaned the cache!). The situation persisted even without css and barebones HTML. After this, i did what you said, just in case (and because it looks nicer!). Thanks for the support!

How can I wrap lines of text in an unordered list?

I have some nested tables in a containing "column" table. The bottom table just contains a multi-line text area where the user can post comments. When the user saves the page I want to capture each separate line of comment as an element in an unordered list. Here is the HTML:
<td><strong>Key Messages:</strong><asp:Label runat="server" ID="messagesLabel"></asp:Label>
<div id="messagesDiv" runat="server">
<asp:TextBox runat="server" ID="MessagesTextbox" Width="100%" TextMode="MultiLine" Height="100" Columns="10"></asp:TextBox>
<asp:Button runat="server" ID="clearMessages" Text="Clear Messages" OnClick="ClearMessages_Click" />
</div>
</td>
My problem is that when the unordered list is posted back to the page, very long lines stretch the table beyond the desired width. I would like to have longer comments wrap at some point while still remaining just one element in the list -- ie, one bullet. I have tried setting the MaxLength and Columns properties with no luck.
EDIT:
I tried the first suggestion using:
ul
{
width: 50px;
}
li
{
width: 50px;
}
but it had not effect on the layout. The layout did not change for any value of width.
I tried to use the second solution but I don't have the CSS property "word-wrap" available in my editor. I am programming in Visual Web Developer Express 2010 which supports CSS 2.1 so, as far as I understand, I should be able to set this property but I can't. I tried it anyway and it had no effect on the layout.
EDIT:
Here is how I am creating the list:
XmlDocument xdoc2 = new XmlDocument();
xdoc2.Load(messagesFileLocation);
XmlNodeList messagesList = xdoc2.GetElementsByTagName("message");
if (messagesList.Count > 0)
{
string unorderedList = "<ul>";
for (int i = 0; i < messagesList.Count; i++)
{
XmlNode node = messagesList.Item(i);
string message = node.InnerText.ToString();
unorderedList += "<li>" + message.ToString() + "</li>";
}
unorderedList += "</ul>";
messagesDiv.InnerHtml = unorderedList;
}
The user edits are actually saved to an XML file first and then posted back to the page.
Specify the width of the ul and li elements and your content should wrap. Also set the parent elements to a height:auto; so the height changes as the size of the content increases.
If you post more code, I can give you a better idea.
yes i think its possible check this
http://www.w3schools.com/cssref/css3_pr_word-wrap.asp
and this
http://webdesignerwall.com/tutorials/word-wrap-force-text-to-wrap

color of apache wicket component

I have the following code:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd"
xml:lang="en" lang="en">
<body>
<wicket:panel>
<div wicket:id="serviceListContainer">
<table>
<tr wicket:id="serviceListView">
<td>
<span wicket:id="service.name"></span>
</td>
<td>
<span wicket:id="service.state"></span> <!-- I WANT TO COLOR THIS PART IN RED! -->
</td>
</tr>
</table>
</div>
</wicket:panel>
</body>
</html>
How can I change the color of the output text that will replace "service.state"? I tried with <font color="red"> but it didn't make any difference.
Thanks!
Other answers have indicated how you can add the style="..." attribute in the HTML template. If, on the other hand, that you do not wish to do this statically (say, you need to calculate the color and then add it to the component), you should add an AttributeModifier to the Component1.
Example (untested):
Label l = new Label("service.state", ...);
IModel<String> colorModel = new AbstractReadOnlyModel<String>() {
public String getObject() {
return "color: red;"; // Dummy example
}
}; // Some model, holding a string representation of a CSS style attribute (e.g., "color: ...;") based on some date and/or calculation
l.add(new AttributeModifier("style", true, colorModel);
You could even use a SimpleAttributeModifier if you don't need the pull-based model:
Label l = new Label("service.state", ...);
String styleAttr = "color: red;";
l.add(new SimpleAttributeModifier("style", styleAttr));
1) Provided that setRenderBodyOnly(true) has not been called. That would remove the wrapping <span> element from the output.
From W3Schools:
The <font> tag is deprecated in HTML 4, and removed from HTML5.
The World Wide Web Consortium (W3C) has removed the tag from
its recommendations. In HTML 4, style sheets (CSS) should be used
to define the layout and display properties for many HTML elements.
Use styles, even if it is inline styling, instead:
<span style="color:red">this is red text</span>
If you're using Wicket you'll want to make sure you're not using setRenderBodyOnly(true) on the Component with id service.state, as this would strip the <span> tag with the style.
<span wicket:id="service.state" style="color:red"></span>
or better is to use proper css classes

How can I set a <td> width to visually truncate its displayed contents?

I'm trying to set a column width of a table, which works fine until it gets to the point where child elements would be visually truncated ... then it won't size any smaller. ("visually truncated"-what doesn't fit appears to be hidden behind the next column)
Here is some sample code to demonstrate my problem:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<script language="javascript" type="text/javascript">
var intervalID = null;
function btn_onClick()
{
// keep it simple, only click once
var btn = document.getElementById("btn");
btn.disabled = true;
// start shrinking
intervalID = window.setInterval( "shrinkLeftCell();", 100);
}
function shrinkLeftCell()
{
// get elements
var td1 = document.getElementById("td1");
var td2 = document.getElementById("td2");
// initialize for first pass
if( "undefined" == typeof( td1.originalWidth))
{
td1.originalWidth = td1.offsetWidth;
td1.currentShrinkCount = td1.offsetWidth;
}
// shrink and set width size
td1.currentShrinkCount--;
td1.width = td1.currentShrinkCount; // does nothing if truncating!
// set reporting values in right cell
td2.innerHTML = "Desired Width : ["
+ td1.currentShrinkCount
+ "], Actual : ["
+ td1.offsetWidth + "]";
// Stop shrinking
if( 1 >= td1.currentShrinkCount)
window.clearInterval(intervalID);
}
</script>
</head>
<body>
<table width="100%" border="1">
<tr>
<td id="td1">
Extra_long_filler_text
</td>
<td id="td2">
Desired Width : [----], Actual : [----]
</td>
</tr>
</table>
<button id="btn" onclick="btn_onClick();">Start</button>
</body>
</html>
I've tried setting the width in different ways including
td1.offsetWidth = td1.currentShrinkCount;
and
td1.width = td1.currentShrinkCount + "px";
and
td1.style.width = td1.currentShrinkCount + "px";
and
td1.style.posWidth = td1.currentShrinkCount;
and
td1.scrollWidth = td1.currentShrinkCount;
and
td1.style.overflow = "hidden";
td1.width = td1.currentShrinkCount;
and
td1.style.overflow = "scroll";
td1.width = td1.currentShrinkCount;
and
td1.style.overflow = "auto";
td1.width = td1.currentShrinkCount;
I realize, I could probably use DIV's, but I'd prefer not to since the table is being generated from a bound control, and I don't really want to get into rewriting asp.net controls.
Having said that, I thought as a last ditch effort, I could at least wrap each 'td1's contents with a DIV, and the overflow would take care of things, but even replacing
<td id="td1">
Extra_long_filler_text
</td>
with
<td id="td1" style="overflow:hidden;">
<div style="margin=0;padding:0;overflow:hidden;">
Extra_long_filler_text
</div>
</td>
didn't work.
Does anybody know how I can set a width to visually truncate its contents?
BTW-My target browser is IE7. Other browsers are not important right now, since this is an internal app.
I just tried adding table-layout: fixed; to the <table> and it worked for me on IE7.
In a fixed table layout, the horizontal layout only depends on the table's width, the width of the columns, and not the content of the cells
Check out the documentation.
CSS Solution
"Truncate" may not be the word you are looking for. You may just want to hide the overflowing characters. If that is the case, look into the css-property "overflow," which will hide any content extending past the declared limits of its container.
td div.masker {
height:25px;
width:100px;
overflow:hidden;
}
<td>
<div class="masker">
This is a string of sample text that
should be hidden when it reaches out of the bounds of the parent controller.
</div>
</td>
Javascript Solution
If you are indeed wanting to truncate the text within the container, you will have to remove one character from the right, test the width of the parent, and continue to remove characters and testing the parent width until the parent width matches the declared width.
while (parent.width > desiredWidth) {
remove one char from right-side of child-text
}
<td id="td1" style="overflow-x:hidden;">
Should be
<td id="td1" style="overflow:hidden;">
overflow-x is a Microsoft CSS attribute, which is now part of CSS3. I would stick with the older overflow attribute.
EDIT:
As Paolo mentions you also need to add table-layout:fixed; and specify the width of the table. A full example follows.
<html>
<head>
<style>
table
{
table-layout:fixed;
width:100px;
}
td
{
overflow:hidden;
width:50px;
border-bottom: solid thin;
border-top:solid thin;
border-right:solid thin;
border-left:solid thin;
}
</style>
</head>
<body>
<table>
<tr><td>Some_long_content</td><td>Short</td></tr>
</table>
</body>
</html>
As far as I know, a table cell will always stretch to accommodate its contents.
Have you thought about using Javascript to dynamically truncate the data in table cells that have more than a certain amount of content? Alternatively, you could do this more easily server side.
You could use jQuery or plain javascript to surround the content of your cell(s) with div tags and set a fixed width and overflow:hidden on those instead. Not beautiful, but it'd work.
ETA:
<td><div style="width:30px;overflow:hidden">Text goes here</div></td>
Since the DIV is the bounding element, that's where the width should be set.