I'm having problems with printing some text on a A4 paper that has 24 labels.
Basically, in every row there are 3 labels in which comes the name, surname and adress of a person and that label will be used for mails ( it's a sticky label that is sticked on a mail).
So this is the paper. Its characteristics:
There are 10 rows.
The first and last row are the smallest and have height:0.5mm;.
In first and last row there are no cells.
All the rest rows have height:36mm;.
All the cells have width:70mm; and height:36mm;.
In every cell comes a text that is text-align:center; and vertical-align:middle;.
I'm using normalize.css for css reset.
CSS
html,body,table{
width: 100%;
height: 100%;
}
.first, .last{
width: 100%;
height: 5mm;
}
.row{
width: 100%;
height: 36mm;
}
.cell{
width: 70mm;
height: 36mm;
text-align: center;
vertical-align: middle;
}
I'm using Chrome and I turned off the margins on printing.
But still, the last two rows are printed on the next page.
I need all 10 rows on the same page and that their position is fixed ( doesn't shift ) in case if there are multiple pages.
How to fix/achieve that ? Or is there a simpler solution ?
Here is an example of the code.
I've used FPDF class to create a pdf for my labels.
require_once ABSPATH . '/path/to/fpdf.php';
class PDF_MC_Table extends FPDF{
var $widths;
var $aligns;
function SetWidths($w){
//Set the array of column widths
$this->widths=$w;
}
function SetAligns($a){
//Set the array of column alignments
$this->aligns=$a;
}
function Row($data){
//Calculate the height of the row
$nb=0;
for($i=0;$i<count($data);$i++)
$nb=max($nb,$this->NbLines($this->widths[$i],$data[$i]));
$h = 36;// again trial and error until you fnd the desired height of your label
//Issue a page break first if needed
$this->CheckPageBreak($h);
//Draw the cells of the row
for($i=0;$i<count($data);$i++){
$w=$this->widths[$i];
$a=isset($this->aligns[$i]) ? $this->aligns[$i] : 'L';
//Save the current position
$x=$this->GetX();
$y=$this->GetY();
//Draw the border. reset the parameters of the function below as you desire.
$this->Rect($x,$y,$w,$h);
//Print the text. reset the parameters of the function below as you desire. changing the values will resize the boxs.
$this->MultiCell($w,3,$data[$i],0,$a);
//Put the position to the right of the cell. reset the parameters of the function below as you desire. changing the $x and $y will shift the cells.
$this->SetXY($x+$w,$y);
}
//Go to the next line
$this->Ln($h+3);
}
function CheckPageBreak($h){
//If the height h would cause an overflow, add a new page immediately
if($this->GetY()+$h>$this->PageBreakTrigger)
$this->AddPage($this->CurOrientation);
}
function NbLines($w,$txt){
//Computes the number of lines a MultiCell of width w will take
$cw=&$this->CurrentFont['cw'];
if($w==0)
$w=$this->w-$this->rMargin-$this->x;
$wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
$s=str_replace("\r",'',$txt);
$nb=strlen($s);
if($nb>0 and $s[$nb-1]=="\n")
$nb--;
$sep=-1;
$i=0;
$j=0;
$l=0;
$nl=1;
while($i<$nb){
$c=$s[$i];
if($c=="\n"){
$i++;
$sep=-1;
$j=$i;
$l=0;
$nl++;
continue;
}
if($c==' ')
$sep=$i;
$l+=$cw[$c];
if($l>$wmax){
if($sep==-1){
if($i==$j)
$i++;
}
else
$i=$sep+1;
$sep=-1;
$j=$i;
$l=0;
$nl++;
}
else
$i++;
}
return $nl;
}
}
$pdf=new PDF_MC_Table();
$pdf->SetMargins(4, 2);
$pdf->AddPage();
$pdf->SetFont('Arial','',8);
// displays the empty row in the top
$pdf->SetRightMargin(2);
$pdf->SetLeftMargin(4);
$pdf->Cell(0,10,'',1);
$pdf->Ln(10);
$pdf->SetWidths(array(50,50,50));// these are the widths of your cells. this is a trial and error process. increase the values until you find the suitable ones.
$count = 0;
$lables = array(); // your labels array
$l = array();
$j = 0;
// i used this foreach for breaking my plain array into a 2dimentional array- an array of arrays consisting 3 labels in each.
foreach($lables as $i=>$lbl ){
$l[$j][] = $lbl;
if($i%3==2){$j++;} // $i=0,1,2 > $j=0; $i=3,4,5 > $j=2 etc this will break the main labels array as 2D array.
}
// displays the empty row in the bottom.
$pdf->Ln(1);
$pdf->Cell(0,10,'',1);
$pdf->Output();
for further information about the class and methods please refer to http://www.fpdf.org/ At best you need to understand three methods- Multicell(), Cell() and Rect() methods. There are pretty nice explanation of these methods with examples in the site.
Here i've posted my solution and i've changed some code based on your problem. Most of the things are self explanatory. If you need further assist please feel free to comment. Thanks.
reduce the cell and row height sizes if you are no using the sizes in specific, what happens even after turning off margins, printing data goes out of paper length and printer moves it to another page automatically.
thats the best solution i think you can try.
p.s: these kinds of sticky labels usually come with their design software cd which you usually use to set the margins with preview. if you dont have it, only thing you can do is maually adjust the heights as i told above, print on a normal A4 plain paper and tele with the sticky label paper. I used to do it too... worked for me though
Try borderless printing if your printer supports this feature.
Try adding in the styling below and playing with the #page attributes. More info in #page can be found here: https://developer.mozilla.org/en-US/docs/CSS/#page
I've had good luck using the styles defined below to print perfectly on 8.5x11 documents where I can set my own margin and have consistent results out of the browser.
<style type="text/css" media="print">
#page {
size: auto; /* auto is the initial value */
margin: 0mm; /* this affects the margin in the printer settings */
}
body {
background-color: #fff;
border: solid 1px #000 ;
margin: 0px; /* this affects the margin on the content before sending to printer */
padding: 0.25in 0.5in;
}
</style>
Related
I'm trying to create a game board ( similar to a chess board) for a game in react, and I would like to arrenge the cells (rectangular divs, that take they're value from a state array) in different shapes ( circle, triangle, oval etc) thus creating a board of that shape.
I only know how to arrange divs with CSS flex or grid and that is always rectangular formation (as far as I know)
How can I achieve this?
Any help will be appreciated, thanks!
To dynamically position elements, you need to arrange them in code. We can do that by creating the elements in code, then setting their position programmatically. I'm working off this jsfiddle
First, the HTML:
<div id="my_container" />
It couldn't get simpler. We just want somewhere we can put our elements. Now, for minimal styling, we've got:
#my_container {
position: relative;
width: 200px;
height: 200px;
background-color: powderblue
}
.cell {
background-color: red;
width: 5px;
height: 5px;
position: absolute;
}
I've added colors so that you can see the elements in the generated output, but they're not necessary. What is necessary is those position lines. Making the #container div relative lets it behave nicely with whatever it's placed in, and making each of the .cell divs absolute causes them to be placed on absolute coordinates relative to their parent, which means we can put them where we want. Ok, what's next?
The cells. I assume your cells already exist, but since I don't have them, I've created code to build these cells, shown below:
const container = document.getElementById("my_container");
const number_of_cells = 30;
const cells = [];
for (var i = 0; i < number_of_cells; i++)
{
const newDiv = document.createElement("div");
newDiv.classList.add("cell");
container.appendChild(newDiv)
cells.push(newDiv);
}
The goal here is to put the cells as children of #my_container and also create an array of all the cells. (I think maybe this step is redundant but I don't care...It makes the next step easier.)
function PositionCells(cells, x, y, radius)
{
const incr_angle = (2*Math.PI)/cells.length;
for (let i = 0; i < cells.length; i++) {
var new_x = x+radius * Math.cos(incr_angle * i);
var new_y = y+radius * Math.sin(incr_angle * i);
cells[i].style.left = new_x+'px';
cells[i].style.top = new_y+'px';
}
}
PositionCells(cells, 50,50,30);
Basically what I've done is written a function to position all the cells in an ordered, programmatic way. If you want other shapes, you can pretty easily do the math to come up with where to position them.
And that's it!
I've added a left and right padding to the header of the QTreeView using this QSS code:
QHeaderView::section{padding:7px 15px}
But the content of the columns is not aligned anymore with the headers.
How can I add a padding of 15px ( like in the header ) to the columns' content?
Edit: For some reasons I use delegates to draw the content of the QTreeView, that's why styling the QTreeView::item doesn't work ( like #svlasov suggested ).
painter.translate(15, 0) seems to fix this issue, but a weird effect appears when I select a row: the selection is not continuous.
Something like this:
QTreeView::item { border: 0px; padding: 0 15px; }
This is rude and crude, but it does what you're asking for in a real simple way for a 3 column QTreeWidget.
// Okay, I want to make sure my columns are wide enough for the contents, but I also
// Don't want them squished together. So use resizeColumnToContents, to make them
// as small as they can be to show the contents, then take those widths and add some
// spacing and then set the columns to the new widths.
m_tree->resizeColumnToContents (0);
m_tree->resizeColumnToContents (1);
int w0 = m_tree->columnWidth (0) + 20;
int w1 = m_tree->columnWidth (1) + 20;
m_tree->setColumnWidth (0, w0);
m_tree->setColumnWidth (1, w1);
I'm trying to emulate the CTRL+F functionality from Chrome that highlights matches on the page in the scrollbar, but for certain fields in a form. Using page offsets and percentages, I have blocks of color which correspond to the relative locations of those fields on the page.
In my prototype, the blocks of color sit to the left of the scrollbar. Ideally, they'd sit UNDERNEATH the scrollbar, and the scrollbar's track would be transparent so that it looks like they're part of the scrollbar track.
Can the default scrollbar be set to allow for overflow content to show underneath it (or allow page content to go over it)? I know this could be accomplished if I just rolled my own scroll, but I'd like to use the default ones provided by the browser if at all possible.
It's clearest if you just look at this Prototype.
CSS:
::-webkit-scrollbar {
width: 14px;
height: 18px;
background-color:transparent;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-track-piece {
background:none;
}
::-webkit-scrollbar-thumb {
height: 6px;
border: 4px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
-webkit-border-radius: 7px;
background-color: #333
}
::-webkit-scrollbar-button {
width: 0;
height: 0;
display: none;
}
::-webkit-scrollbar-corner {
background-color: transparent;
}
I thought of rendering the matches on the trackbar similarly to what browsers do today before. The idea is simple by using linear-gradient background for the ::-webkit-scrollbar-track. However I did not try implementing this. Right after reading your question, I've tried it and looks like it's not such easy.
You can use the linear-gradient background OK, but if you try rendering more than 1 match (a line), it sometimes can't be rendered (especially when the window's size is changed) and the line is not rendered smoothly. Such as this seems to be OK:
//render 2 lines, one is at 50px and the other is at 100px
background: linear-gradient(transparent 50px, red 50px, red 51px, transparent 51px,
transparent 100px, red 100px, red 101px, transparent 101px);
but it's not stable, as I said when you try resizing the window, at some size, some line won't be rendered (at least I tried on Opera). When the window's height is large, the line even becomes blurred (not sharp) and thicker. I don't really understand this, because the color stops are set fixedly (by px, not by %). This issue is even worse when the number of lines is larger. You have a linear-gradient with many corresponding color stops. That seems to be a neat way to solve the problem. Just because of the undesired issue, we can't use that approach.
The new approach: So I tried using multi-backgrounds feature instead. Each background just renders 1 line, the background-size is the same for all the background is just about 2px height and the background-position should be different. Here is the equivalent code (to the above clean code) using this approach:
background: linear-gradient(red, red), linear-gradient(red, red);
background-repeat: no-repeat;
background-size: 100% 2px;
background-position: 0 50px, 0 100px;
The new approach of course requires that the browser has to support multi-backgrounds features (looks like just IE8- do not support this cool feature).
So that's almost what you need to solve this problem. Now we need to find how to apply that style using script. We can't select a pseudo-element (or something like that) via script. We can just use the window.getComputedStyle() method to get the read-only style of a pseudo-element. However we always have a way to modify the CSS directly. That's is by using pure JS with the help of document.styleSheets and cssRules. They allow us to insert/remove/modify a rule.
That looks great. But there is still another issue. When changing the style using that method, the style is not applied right (at least it happens to the ::-webkit-scrollbar-track, it may not happen to other elements). Only when you move the mouse over the scrollbar, the new style is applied. I've just found a simple way to invalidate that scrollbar by setting the overflow of document.documentElement (the html) to hidden and set it back to auto. That works almost well.
Here is the code:
var requiredTb = $(".required input");
var invalids = requiredTb;
var offsets = [];
//init offsets to highlight on the trackbar later
requiredTb.each(function() {
offsets.push(($(this).offset().top)/document.body.scrollHeight * 100);
});
//find the rule styling the -webkit-scrollbar-track
//we added in the CSS stylesheet, this is done just 1 time
var sheets = document.styleSheets;
var trackRule;
for(var i = 0; i < sheets.length; i++){
var rules = sheets[i].cssRules || sheets[i].rules;
for(var j = 0; j < rules.length; j++){
var rule = rules[j];
if(rule.selectorText == "::-webkit-scrollbar-track:vertical"){
trackRule = rule; break;
}
}
}
//define an invalidate() method, we need to use this method
//to refresh the scrollbars, otherwise the newly applied style is not affected
window.invalidate = function(){
$(document.documentElement).css('overflow','hidden');
setTimeout(function(e){
$(document.documentElement).css('overflow','auto');
},1);
};
//this is the main function to set style for the scrollbar track.
function setTrackHighlights(positions, color){
positions.sort();//ensure that the input array should be ascendingly sorted.
trackRule.style.cssText = "";
var gradient = "background: ", backPos = "background-position: ";
var winHeight = $(window).height();
$.each(positions, function(i,pos){
gradient += "linear-gradient(" + color + ", " + color + "),";
backPos += "0 " + pos + "%,"
});
gradient = gradient.substr(0,gradient.length-1) + ";";
backPos = backPos.substr(0,backPos.length -1) + ";";
trackRule.style.cssText += gradient + backPos + "background-repeat:no-repeat; background-size:100% 2px";
invalidate();
}
//initially set the highlights on the trackbar
setTrackHighlights(offsets,'red');
//handle the oninput event to update the highlights accordingly
requiredTb.on('input', function(e){
var required = $(this).closest('.required');
var refreshHighlights = false;
if(this.value && !required.is('.ok')) {
required.addClass('ok');
refreshHighlights = true;
invalids = invalids.not(this);
}
if(!this.value && required.is('.ok')) {
required.removeClass('ok');
refreshHighlights = true;
invalids = invalids.add(this);
}
if(refreshHighlights){
offsets.splice(0);
invalids.each(function() {
offsets.push(($(this).offset().top)/document.body.scrollHeight * 100);
});
setTrackHighlights(offsets,'red');
}
});
You have to add an empty ::-webkit-scrollbar-track:vertical rule (we need to deal only with the vertical scrollbar) in the CSS code, it should be appended at the last to override any similar rule before. We can in fact use the insertRule() method (of a CSSRuleList which can be accessed via cssRules property) to add a new rule instead of looping through the styleSheets, and through the cssRules (of each sheet) to find the empty rule ::-webkit-scrollbar-track:vertical.
The code I posted here can be improved, such as you can add another method setTrackHighlights to allow to add more lines (instead of rendering all the lines each time we need to add/remove just 1 line)...
Note that by using the term line, I mean the rendering representation of a match on the trackbar.
Demo
I have a carousel in Bootstrap that displays 4 columns of thumbnails. Here's the carousel in question. If you move to the third page, you can see that the container increases in height in order to accommodate the contents of the thumbnail captions. I've been trying many things such as setting bottom margins, min heights, etc. to get the position of the "View Details" button constant across the entire carousel.
My question is what is the best way to approach this issue? I was thinking somehow making the thumbnail caption height a minimum of 4 or so lines, but I tried that(probably the wrong way) to no avail.
When I add
.caption h4 {
min-height: 2.2em; /* 2 lines as line-height is 1.1 */
}
I get all "View details" at the same level. However, that obviously doesn't treat the problem of captions being even higher. It only works if no caption is higher in fact. (But it IS ok, if you know for sure nothing is going to be higher than your multiple.)
So, instead I apply this little bit of CSS to put a limit from the other side.
.caption h4 {
max-height: 4.4em; /* 4 lines as line-height is 1.1 */
height: 4.4em; /* making it a multiple allows usage of overflow */
overflow: hidden; /* without cutting a line in the middle */
}
If you want to set a max-height equal to the height of the highest of captions dynamically, than you would have to use a little bit of JS:
(function(d) {
var captions = d.querySelectorAll('.caption h4'),
height = 0;
for(var i = 0; i < captions.length; i++) {
height = Math.max(height, captions[i].offsetHeight); // or clientHeight depends on you box model
}
var style = d.createElement('style');
style.type = 'text/css';
style.innerHTML = '.caption h4 { max-height: '+ height +'px; height: '+ height +'px; }'; // they don't need overflow as none of them can overflow;
d.getElementsByTagName('head')[0].appendChild(style);
})(document);
You add this script at the end of body, so that the DOM is already loaded (or somehow trigger it onload).
Important: this snippet is not supported by older browsers because of the querySelectorAll.
And that does the trick when I run it on your site.
I have quite annoying problem with hiding grid columns dynamically. After I hide columns (with long text in cells), the height of grid rows dramatically increases.
Before hide
and after hide operation
As You can see first row is definitely too high. Probably the reason of that behavior is the fact, that I use text wrap in grid cells.
.x-grid-cell-inner { white-space: normal; }
Is there any efficient way to make grid rows, not to change their height after hiding columns (and using textwrap ) ?
I've personally encountered this strange phenomenon before. The problem is caused by Ext JS "hiding" columns by setting the width to 0px.
My solution was to add event listeners to the grid header like this:
// me is the grid
me.headerCt.on({
columnhide: me.removeWordWrapOnHide,
columnshow: me.addWordWrapOnShow,
scope: me
});
Instead of using the existing x-grid-cell-inner class, make a new one like this:
<style type="text/css">
td.grid-cell-wordwrap > div {
white-space: normal; /* may need !important, not sure */
}
</style>
Then the implementation of these two functions did this:
removeWordWrapOnHide: function(headerCt, column){
var me = this,
wordWrapRe = /wordwrap/;
if(column.useWordWrap || wordWrapRe.test(column.tdCls)){
column.tdCls = column.tdCls.replace("grid-cell-wordwrap", "");
column.useWordWrap = true; // Flag to check on show
me.view.refresh();
}
},
addWordWrapOnShow: function(headerCt, column){
var me = this,
wordWrapRe = /wordwrap/;
if(column.useWordWrap && !wordWrapRe.test(column.tdCls)){
column.tdCls = "grid-cell-wordwrap " + column.tdCls;
me.view.refresh();
}
}
Might not be the most efficient way, but it gets the job done.