I have a local environment in Drupal 7 and I am using the Print module with the mPDF library.
When I apply a background local image to an element in the PDF it doesn't show, I don't have a red ex indicating that it cannot find it, but when I call
$mpdf->showImageErrors = true;
my error log tells me that my PDF cannot find the image, I even put the whole link in the background-image property and still, nothing works.
The image is there if I try accessing it in the browser it shows. If I use a link to a random image from google, it works.
Is there something from Drupal preventing access to the image's path?
LATER EDIT:
So this is my CSS & HTML:
.field-name-body blockquote {
background-color: #f9f9f9;
background-image: url("https://local-website.dd:8443/profiles/local/themes/bootstrap_subtheme/img/pdf-icons/quote.png");
/*background-image: url("/<?php //print drupal_get_path('theme', 'bootstrap_subtheme')?>/img/pdf-icons/quote.png");*/
background-position: 24px 24px;
background-repeat: no-repeat;
background-size: 24px;
border: none;
color: #25a898;
font-size: 14px;
line-height: 17px;
margin: 24px 0;
padding: 24px 24px 24px 52px;
}
<div class="field field-name-body field-type-text-with-summary field-label-hidden">
<div class="field-items">
<div class="field-item even">
<div class="field-item even">
<blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec risus nibh, mattis sed mi tincidunt, porta blandit nibh. Maecenas vulputate imperdiet augue, a tempus nulla venenatis vitae. Etiam rhoncus laoreet luctus. Phasellus dolor justo, tincidunt a eleifend vel, ornare nec dolor.</p>
</blockquote>
</div>
</div>
</div>
</div>
I am not doing anything special, my image resides in the path I specified because I can see it locally when I access that address.
The PHP code is the code that resides in the Print module. I only added the showImageErrors line. Images that are added by a content editor are visible, images added in HTML manually or CSS aren't.
function print_pdf_mpdf_print_pdf_generate($html, $meta, $paper_size = NULL, $page_orientation = NULL) {
module_load_include('inc', 'print', 'includes/print');
$pdf_tool = explode('|', variable_get('print_pdf_pdf_tool', PRINT_PDF_PDF_TOOL_DEFAULT));
// Version 7 of the mpdf library uses a composer autoloader.
// Also there no longer is way to truly detect the library version, so this
// seems like the best alternative.
$mpdf_version_7_plus = strpos($pdf_tool[1], 'autoload.php') !== FALSE;
if (empty($paper_size)) {
$paper_size = variable_get('print_pdf_paper_size', PRINT_PDF_PAPER_SIZE_DEFAULT);
}
if (empty($page_orientation)) {
$page_orientation = variable_get('print_pdf_page_orientation', PRINT_PDF_PAGE_ORIENTATION_DEFAULT);
}
$images_via_file = variable_get('print_pdf_images_via_file', PRINT_PDF_IMAGES_VIA_FILE_DEFAULT);
$config = array();
if ($mpdf_version_7_plus) {
$config['tempDir'] = drupal_realpath('public://print_pdf/print_pdf_mpdf/');
}
else {
// Deprecated since mpdf v7.x.
if (variable_get('print_pdf_autoconfig', PRINT_PDF_AUTOCONFIG_DEFAULT)) {
if (!defined('_MPDF_TTFONTDATAPATH')) {
define('_MPDF_TTFONTDATAPATH', drupal_realpath('public://print_pdf/print_pdf_mpdf/ttfontdata/'));
}
if (!defined('_MPDF_TEMP_PATH')) {
define('_MPDF_TEMP_PATH', drupal_realpath('public://print_pdf/print_pdf_mpdf/tmp/'));
}
}
}
$tool_path = DRUPAL_ROOT . '/' . $pdf_tool[1];
if (file_exists($tool_path)) {
require_once $tool_path;
}
else {
watchdog('print_pdf', 'Configured PDF tool does not exist at path: %path', array('%path' => $tool_path), WATCHDOG_ERROR);
throw new Exception("Configured PDF tool does not exist, unable to generate PDF.");
}
$format = ($page_orientation == "landscape") ? $paper_size . "-L" : $paper_size;
// Try to use local file access for image files.
$html = _print_access_images_via_file($html, $images_via_file);
// Set document information.
if ($mpdf_version_7_plus) {
$config['mode'] = 'utf-8';
$config['format'] = $format;
$mpdf = new \Mpdf\Mpdf($config);
}
else {
$mpdf = new mPDF('UTF-8', $format);
}
if (isset($meta['name'])) {
$mpdf->SetAuthor(strip_tags($meta['name']));
}
$mpdf->SetCreator(variable_get('site_name', 'Drupal'));
$mpdf->WriteHTML($html);
// Try to recover from any warning/error.
ob_clean();
return $mpdf->Output('', 'S');
}
Related
I am trying to learn React through a Udemy video and got stuck at the media queries. When I add the given #media(min-width:992px){ .NewBlogCard{width:50%;} statement into my editor and save it, nothing changes in my browser. All three cards still take up the whole screen instead of half of the screen. Does anyone know what I am doing wrong? Please see code to all my files below. The above statement is in the file called BlogCard.module.css.
BlogCard.module.css
.NewBlogCard{
margin: 16px;
padding: 16px;
box-sizing: border-box;
border-radius: 5px;
box-shadow: 0 2px 5px #CCC;
background-color: lightblue;
};
#media(min-width:992px+1px){
.NewBlogCard{
width:50%;
}
}
BlogCard.js
import React from "react";
import { dumplogs } from "./Utils";
import classes from "./BlogCard.module.css";
const BlogCard = (properties) => {
dumplogs(properties);
return (
<div className={classes.NewBlogCard}>
<h3>{properties.title}</h3>
<p>{properties.description} </p>
</div>
);
};
export default BlogCard;
App.js
//import "./App.css";
import BlogCard from "./BlogCard";
import { isArrayEmpty } from "./Utils";
import classes from "./BlogCard.module.css";
function App() {
const blogArr = [
{
id: 1,
title: "Blog Title 1",
description:
"Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor",
},
{
id: 2,
title: "Blog Title 2",
description:
"Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor",
},
{
id: 3,
title: "Blog Title 3",
description:
"Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor",
},
];
const blogCards = isArrayEmpty(blogArr)
? []
: blogArr.map((item, pos) => {
console.log(item);
return (
<BlogCard
key={pos}
title={item.title}
description={item.description}
id={item.id}
/>
//<div className="BlogCard" key={item.id}>
//<h3>{item.title}</h3>
// <p>{item.description} </p>
// </div>
);
});
return <div className="App">{blogCards}</div>;
}
export default App;
Utils.js
const isArrayEmpty = (arr) => {
if (arr !== undefined && arr !== null && arr.length > 0) {
return false;
}
return true;
};
const dumplogs = (message) => {
console.log(message);
//sends it to a tool for tracking
};
export { isArrayEmpty, dumplogs };
I have added your code in codesandBox and made some changes please check
https://codesandbox.io/s/quizzical-roentgen-54u1en?file=/src/App.js
Thanks
In your given code you have declared as
#media(min-width:992px+1px){
.NewBlogCard{
width:50%;
}
But in your question, you mentioned you need
#media(min-width:992px)
Please have a look at that and try to remove the +1px in the media query
If you need all the cards to be in 50% in all screens, then you have to add
.NewBlogCard {
width:50%;
}
without any media query.
If you need the cards to be in 50% above some screensize only then, you have to use media queries. Please go through this link to learn more about media query
For example:
#media screen and (max-width: 768px) {
.NewBlogCard {
width:50%;
}
}
If you are using chrome browser and changes are not affecting better to do a hard reload and clear cache.
See Shahil Mishra's last comment...it fixes the problem with the code. Thanks again Shahil Mishra
I have a project where some pages have titles with a large font size. The title is dynamic and can be both one or 3-4 lines. For aesthetical reasons, I would love the title to be both centered and arrange the lines so that the longest (broadest) line goes to the bottom and the shortest goes to the top. Like the shape of a pyramid.
I don't know if this is possible in pure CSS or how I should get around this?
Any suggestions? :-)
How about shape-outside? We can limit the text boundaries with two triangle shapes, one on the left and another on the right
Do note: despite of the boundaries centering the text doesn't yield the desired result, but on the contrary, justifying the text is not ideal either - the last line is not centered
div{
text-align:justify;
font-size:20px;
padding:15px;
background:rgba(200,200,100,0.5);
}
left-shape {
shape-outside: polygon(0 0, 100% 0, 0 50%);
width: 250px;
height: 500px;
float:left;
}
right-shape {
shape-outside: polygon(100% 0, 0% 0, 100% 50%);
width: 250px;
height: 500px;
float:right;
}
<left-shape></left-shape> <right-shape></right-shape>
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
Possible javascript solution
If you can't find CSS solutions, here's a "somewhat solution" using javascript. It may not be the best approach and may have problems if the screen width decreases because we split the two strings using a breakpoint at the end. So this solution might work for you if you have enough spacing for the title, but you might want to consider using whitespace: nowrap; with this solution.
Try changing the offset to find the best result for yourself.
// Get title element by ID:
var title = document.getElementById('title');
// Get content of the title:
var content = title.innerText;
// Where you want the text to be splitted from,
// 2 = middle, 3 = one third;
var offset = 2.5;
// Divide content by previously set amount:
var divided = Math.floor(content.length / offset);
// This gives the number of characters
// in the top part of the title
var count = content.indexOf(' ', divided);
// First part of the string,
// goes from start to "count" amount
var x = content.substring(0, count);
// Second part of the string, after count amount:
var y = content.substring(count);
// Place text back to the title,
// Using <br> might cause issues on mobile,
// but it's a rough idea what could be done.
title.innerHTML = x + "<br>" + y;
#title{
text-align: center;
}
<h1 id="title">This is an example of the line break I want</h1>
I am no JQuery-wonder, but I have made this solution that fulfills my expectations for the alignment I requested. If your eyes hurt from this snippet, please give some suggestions for optimization. I already know that this won't work after resizing flexible elements.
I would of course prefer a solution made from pure CSS, so if you are listing W3C, the case text-align: pyramid; or even text-align: simon; (wink) is now officially requested.
$('#title').each(function(){
var words = $(this).text().split(/\s+/);
var total = words.length;
var originalWidth = $(this).css("width");
var maxWidth = $(this).width();
var tempText = $(this);
var string = "";
var currentLineString = "";
$(this).empty();
tempText.css("width", "auto");
for (i = 0; i < total; i++){
var newString = words[total-i-1] + " " + currentLineString;
tempText.text(newString);
if (tempText.width() >= maxWidth) {
tempText.text(currentLineString);
maxWidth = tempText.width();
string = currentLineString + "<br>" + string;
currentLineString = "";
i--;
} else {
currentLineString = newString;
}
}
string = currentLineString + "<br>" + string;
tempText.css("width", originalWidth);
$(this).html(string);
});
I have a CSS file with:
label {
-fx-font-size: 10px;
-fx-font-weight: bold;
-fx-text-fill: #333333;
-fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );
}
If I want to have one label with another style, how can I do it?I'm trying it with the following code, but it does not change.
l_mov.setFont(Font.font("calibri", FontWeight.BOLD, FontPosture.REGULAR,
25));
Even if the Chao-Wen chen solution seems to be the best one for your case : (using the id). You should keep in mind that you also can use your own css styleclass.
Here is an example to be more exhaustive :
public class CssApp extends Application {
#Override
public void start(Stage stage) throws Exception {
VBox vb = new VBox(10.0);
// Title
Label title = createTitle("Title");
// SubTitle 1
Label subtitle1 = createSubTitle("SubTitle1");
//Standard text
Label lorem = new Label("Lorem ipsum dolor sit amet, mea eu nibh sonet accusam, mea dicunt oblique et. Mei purto efficiantur ne, quas audiam consulatu at eum. Ea sit dicta zril, adipisci praesent pertinacia ei his.");
// SubTitle 2
Label subtitle2 = createSubTitle("SubTitle2");
//Standard text
Label ipsum = new Label("Amet dictas consequat ut vix, maluisset hendrerit vim ex, ne pro tale aliquid accusata. Mea porro aperiri voluptua te, case lorem per eu.");
Label specificText = new Label("I am a specific text with a particular styling.");
specificText.setId("specific");
vb.getChildren().addAll(title, subtitle1, lorem, subtitle2, ipsum, specificText);
Scene scene = new Scene(vb, 500.0, 400.0);
scene.getStylesheets().add(this.getClass().getResource("style.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
private static Label createTitle(String pText) {
Label newLabel = new Label(pText);
newLabel.getStyleClass().add("label-title");
return newLabel;
}
private static Label createSubTitle(String pText) {
Label newLabel = new Label(pText);
newLabel.getStyleClass().add("label-subtitle");
return newLabel;
}
}
And here is the css stylesheet. Please note that the order of your styleclass is important.
.label {
-fx-font-family: serif;
-fx-font-size: 12.0;
-fx-font-weight: normal;
-fx-text-fill: black;
}
.label-title {
-fx-font-family: sans-serif;
-fx-font-size: 50.0;
-fx-font-weight: bold;
-fx-text-fill: red;
}
.label-subtitle {
-fx-font-family: monospace;
-fx-font-size: 19.0;
-fx-font-style: oblique;
-fx-font-weight: normal;
-fx-text-fill: blue;
}
#specific {
-fx-text-fill: green;
}
That deals with this kind of example (this a simple one only for demonstration) :
For this example I only use one stylesheet, but a different stylesheet could be applied for a specific panel (this one will override all the ancestor ones if same style class are provided).
If you have two labels with different styles, maybe you can use two different 'id' to identify them.
I was able to hide everything but pace until the page had loaded when installing pace.js with eager.io.
However, when using bower to install the plugin and downloading the css theme, I was unable to figure out how to do this.
I fixed this by adding this css
body > :not(.pace),body:before,body:after {
-webkit-transition:opacity .4s ease-in-out;
-moz-transition:opacity .4s ease-in-out;
-o-transition:opacity .4s ease-in-out;
-ms-transition:opacity .4s ease-in-out;
transition:opacity .4s ease-in-out
}
body:not(.pace-done) > :not(.pace),body:not(.pace-done):before,body:not(.pace-done):after {
opacity:0
}
The previous answer works in most cases but if for any reason pace.js is disabled, your body will keep its opacity to 0 and your content won't be shown. The following rules avoid this problem:
.pace-running > :not(.pace) {
opacity: 0;
}
.pace-done > :not(.pace) {
opacity: 1;
transition: opacity .5s ease;
}
Then, up to you to add prefixes or pseudo-classes…
After much trial-and-error, I solved this with a JS-only approach. Here's how it works:
When the start or restart Pace events happen, aka when the loading indicator is shown, a full-screen div is appended
This effectively hides the page content, then prevents page interaction, like button clicks
When the hide Pace event happens, aka when the loading indicator is hidden, the full-screen div is removed
UI Note: The OP said "hide everything" which is why the div has an opacity of 1. But, my personal preference is to still show the page, just "grayed out". So I set the opacity to 0.7 or so.
Here's a minimal, reproducible example. It assumes these files are side-by-side in the same directory:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Pace Playground</title>
<!-- Start of required code -->
<script src="https://cdn.jsdelivr.net/npm/pace-js#latest/pace.min.js"></script>
<script src="./pace-config.js"></script>
<link href="./pace-theme.css" rel="stylesheet" />
<!-- End of required code -->
</head>
<body>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
</body>
</html>
pace-config.js
(() => {
const paceZIndex = 2000;
const id = "page_interaction_blocker";
const appendBlocker = () => {
if (document.getElementById(id)) {
return;
}
const el = document.createElement("div");
el.id = id;
el.style.position = "fixed";
el.style.top = 0;
el.style.bottom = 0;
el.style.left = 0;
el.style.right = 0;
el.style.zIndex = paceZIndex - 1;
el.style.backgroundColor = "#fff";
el.style.opacity = 1;
document.body.appendChild(el);
};
const removeBlocker = () => {
const el = document.getElementById(id);
if (el) {
el.remove();
}
};
Pace.on("start", appendBlocker);
Pace.on("restart", appendBlocker);
Pace.on("hide", removeBlocker);
})();
pace-theme.css
Pick a theme from https://codebyzach.github.io/pace/
there, I am creating something like the screenshot below for a person's introduction. But at the bottom I would like to align the image and the paragraph at the bottom, so I would have the text wrap around the image like the second image. Is that possible in CSS?
<div class="ppl-detail">
<img class="ppl-proj" src="images/who/pure-systems.png">
<p class="ppl-text">Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo.</p>
</div>
Thank you!
You can use the "float: left" in css.
img {
float:left;
border:1px dotted black;
margin:0px 0px 15px 20px;
}
Check this http://jsfiddle.net/26WZe/
This is not possible with CSS alone unless you're going to put the image in the middle of the text like #ashis-kumar's answer. I wrote some javascript (no third party library required, but could easily be rewritten to work with jQuery):
var util = {
iterations: 0,
fontSize: 20,
wrapWords: function(el) {
var words = el.textContent.split(' ');
for ( var i = 0, l = words.length; i < l; ++i ) {
words[i] = '<span class="word">' + words[i] + ' </span>';
}
el.innerHTML = words.join('');
return document.querySelectorAll('span.word');
},
checkOffsets: function(words, img, newContainer, origContainer) {
var top = img.offsetTop;
for ( var i = 0, l = words.length; i < l; ++i ) {
origContainer.appendChild(words[i]);
}
origContainer.appendChild(newContainer);
for ( i = 0, l = words.length; i < l; ++i ) {
var word = words[i];
if ( word.offsetTop + word.offsetHeight >= top ) {
newContainer.appendChild(word);
}
}
if ( Math.abs(img.offsetTop - newContainer.offsetTop) > this.fontSize || img.offsetTop < newContainer.offsetTop ) {
this.iterations++;
if ( this.iterations < 10 ) {
this.checkOffsets(words, img, newContainer, origContainer);
}
}
}
};
var img = document.querySelector('img');
var p = document.querySelector('p');
var words = util.wrapWords(p);
var pushContainer = document.createElement('div');
pushContainer.classList.add('push');
p.appendChild(pushContainer);
util.checkOffsets(words, img, pushContainer, p);
See it in action
I wrote it pretty quickly, but what it basically does is wraps all the words in the paragraph with a <span> so that finding its offset is easier. Then it creates another element for all the words that will be next to the image. It then goes through them until it finds the correct number of words to go into it (it is recursive in case there's too much space or not enough). There are probably some optimizations that can be done, but it's pretty quick.
Add a "float" selector to your image:
<img class="ppl-proj" src="images/who/pure-systems.png" style="float:left; padding-right:10px;">
That will get you started. You might need to tweak it a bit, but if you lookup how to use floats, you'll figure it out.
Here is a good learning resource:
http://css.maxdesign.com.au/floatutorial/
I did it this way: <input type='button' style='position:fixed' top:94%; left:82%; width:100px' value='
you can position this way <div>, <p> and so on.