I am using tc pdf library to generate pdf.
I have set custom hedar and footer like below
class CustomTcpdf extends \TCPDF {
public function Header() {
$headerData = $this->getHeaderData();
$this->writeHTML($headerData['string'], true, false, true, false, '');
}
public function Footer() {
// Position at 15 mm from bottom
$this->SetY(-50);
$image_file = 'footer.png';
$this->Image($image_file, 15, 250, 183);
}
}
Following is the code for creation
$tcpdf = new CustomTcpdf();
$tcpdf->setHeaderData($ln = '', $lw = 0, $ht = '', $pdf_header_data, $tc = array(0, 0, 0), $lc = array(0, 0, 0));
$tcpdf->AddPage('A4', 'Portrait');
$tcpdf->SetFooterMargin(60);
//$tcpdf->setHeaderMargin(40);
$tcpdf->SetAutoPageBreak(TRUE, 0);
$tcpdf->SetY(70);
$tcpdf->writeHTML($content, true, false, true, TRUE, '');
$tcpdf->Output('HAKO.pdf', 'I');
output for footer is as below
Expected output is,
Instant of overlapping the content should come on next page.
I use the following code in header function like below
class CustomTcpdf extends \TCPDF {
public function Header() {
$headerData = $this->getHeaderData();
$this->writeHTML($headerData['string'], true, false, true, false, '');
$this->SetTopMargin(70);
}
}
With the help of this->SetTopMargin(70) solve the header problem
and for footer $tcpdf->SetAutoPageBreak(TRUE, 65); 65 is margin from bottom with this 2 mothod overlapping issue will be solve.
Related
There are 2 examples in the Matterport SDK for Embeds documentation to show how to place Mattertags in a scene:
The Intersection Inspector which only allows you to see coordinates for placing a Mattertag where the cursor is if you wait a little bit ... Not very user friendly, you need to copy the coordinates manually in your program.
The Transient Tags Editor which enable you to interactively place multiple Mattertags visually, edit them and then to extract them easily in a JSON file ...
I was wondering how to reproduce the Transient Tags Editor visual UX as I would like to use it in an application.
Insert Mattertags into the model visually
The source code of the app of the Transient Tags Editor is privately hosted on github (Maybe because it doesn't run perfectly on Firefox?), unlike the source code of the Intersection Inspector which is publicly hosted on JSFiddle.
But the user friendliness of the Transient Tags Editor intrigued me and I wanted to understand the difference between the two tools Matterport SDK provides to find out Mattertags coordinates.
How the Intersection Inspector works
The Intersection Inspector uses a timer to display a button at the position of the Pointer when the user does not move the pointer for more than one second. The user can then click the button to see the Mattertag coordinates and copy them manually ...
To achieve that, it needs the current Camera position, which it obtains by observing the camera's pose property:
var poseCache;
mpSdk.Camera.pose.subscribe(function(pose) {
poseCache = pose;
});
Also, it needs the current Pointer position, which it obtains by observing the pointer's intersection property:
var intersectionCache;
mpSdk.Pointer.intersection.subscribe(function(intersection) {
intersectionCache = intersection;
intersectionCache.time = new Date().getTime();
button.style.display = 'none';
buttonDisplayed = false;
});
※ An intersection event is triggered the user moves the pointer, so we hide the button to make sure it is not displayed before the one second delay is over.
Then, a timer is set up using setInterval() to display the button at the right time:
setInterval(() => {
// ...
}, 16);
In the timer callback, we check wether all the conditions to display the button are met ...
First, check we have the information we need:
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
// ...
}, 16);
Then, check one second has elapsed since the last intersection event was received, or we wait the next tick to check again:
var delayBeforeShow = 1000;
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
const nextShow = intersectionCache.time + delayBeforeShow;
if (new Date().getTime() > nextShow) {
// ...
}
}, 16);
Finally, we check the button is not already being displayed:
var delayBeforeShow = 1000;
var buttonDisplayed = false;
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
const nextShow = intersectionCache.time + delayBeforeShow;
if (new Date().getTime() > nextShow) {
if (buttonDisplayed) {
return;
}
// ...
}
}, 16);
Once the conditions are met, we can display the button using Conversion.worldToScreen() to get the screen coordinate of the pointer :
// ...
setInterval(() => {
// ...
if (new Date().getTime() > nextShow) {
// ...
var size = {
w: iframe.clientWidth,
h: iframe.clientHeight,
};
var coord = mpSdk.Conversion.worldToScreen(intersectionCache.position, poseCache, size);
button.style.left = `${coord.x - 25}px`;
button.style.top = `${coord.y - 22}px`;
button.style.display = 'block';
buttonDisplayed = true;
}
}, 16);
The button is a simple HTML button hidden by default using display: none; and positioned relative to the body with position: absolute;.
When the user clicks the button, the current coordinates of the pointer are displayed in a <div> tag above the <iframe> and the button is hidden:
button.addEventListener('click', function() {
text.innerHTML = `position: ${pointToString(intersectionCache.position)}\nnormal: ${pointToString(intersectionCache.normal)}\nfloorId: ${intersectionCache.floorId}`;
button.style.display = 'none';
iframe.focus();
});
The coordinates are formatted using the following function:
function pointToString(point) {
var x = point.x.toFixed(3);
var y = point.y.toFixed(3);
var z = point.z.toFixed(3);
return `{ x: ${x}, y: ${y}, z: ${z} }`;
}
Now, let's see how the easier-to-use and user-friendlier Transient Tags Editor interface works ...
How the Transient Tag Editor works
The Intersection Inspector is enough if you just have a few __Mattertag__s to set permanently in a few models in your application. But if you need your users to set tags interactively in models, something like the Transient Tags Editor's GUI is a better starting point.
The main advantage of using the Transient Tags Editor is that you can see how the Mattertag will be displayed before creating it and! That allows you to place the tag precisely without trial and error ...
To add a tag, you must click on the "Place New Tag" button to enter the "add tag" mode, then you can place one new tag anywhere you want.
We will only focus on that aspect of the editor and produce a simplified code sample that only add tags:
As the user move a tag along the pointer when in "add tag" mode, the first step is to create a new tag and place it. Let's create a function for that using Mattertag.add():
function addTag() {
if(!tag){
mpSdk.Mattertag.add([{
label: "Matterport Tag",
description: "",
anchorPosition: {x: 0, y: 0, z: 0},
stemVector: {x:0, y: 0, z: 0},
color: {r: 1, g: 0, b: 0},
}])
.then((sid) => {
tag = sid[0];
})
.catch( (e) => {
console.error(e);
})
}
}
Then we will have to place the tag at a position near the pointer, and update its position as the user moves the pointer, so let's create a function for that using Mattertag.editPosition():
function updateTagPos(newPos, newNorm=undefined, scale=undefined){
if(!newPos) return;
if(!scale) scale = .33;
if(!newNorm) newNorm = {x: 0, y: 1, z: 0};
mpSdk.Mattertag.editPosition(tag, {
anchorPosition: newPos,
stemVector: {
x: scale * newNorm.x,
y: scale * newNorm.y,
z: scale * newNorm.z,
}
})
.catch(e =>{
console.error(e);
tag = null;
});
}
As you can see the updateTagPos() function takes 3 parameters:
newPos: the new anchor position for the Mattertag.
newNorm: an optional new stem vector for the Mattertag.
scale: an optional new scale for the stem of the Mattertag.
To update the tag position as the user moves the pointer, let's observe the pointer's intersection property to call updateTagPos():
mpSdk.Pointer.intersection.subscribe(intersectionData => {
if(tag){
if(intersectionData.object === 'intersectedobject.model' || intersectionData.object === 'intersectedobject.sweep'){
updateTagPos(intersectionData.position, intersectionData.normal);
}
}
});
To place the new tag, the user simply clicks their mouse button, the Transient Tags Editor provides its own version of the document.activeElement method for intercepting clicks on the <iframe> (but does not work with Firefox so the editor use a quite complex workaround):
function focusDetect(){
const eventListener = window.addEventListener('blur', function() {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
setTimeout(function(){ window.focus(); }, 0);
}
window.removeEventListener('blur', eventListener );
});
}
But, we will use our version which works better with Firefox (But still stop working after the first click in Firefox for whatever reason):
window.addEventListener('blur',function(){
window.setTimeout(function () {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
window.focus()
addTag();
}
}, 0);
});
Finally, let's the function that navigates to the new tag and opens its billboard, usingMattertag.navigateToTag()
function placeTag(){
if(tag) mpSdk.Mattertag.navigateToTag(tag, mpSdk.Mattertag.Transition.INSTANT);
tag = null;
}
Simple Editor Code Sample
First, the complete JavaScript source code:
"use strict";
const sdkKey = "aaaaXaaaXaaaXaaaXaaaXaaa"
const modelSid = "iL4RdJqi2yK";
let iframe;
let tag;
document.addEventListener("DOMContentLoaded", () => {
iframe = document.querySelector('.showcase');
iframe.setAttribute('src', `https://my.matterport.com/show/?m=${modelSid}&help=0&play=1&qs=1>=0&hr=0`);
iframe.addEventListener('load', () => showcaseLoader(iframe));
});
function showcaseLoader(iframe){
try{
window.MP_SDK.connect(iframe, sdkKey, '3.10')
.then(loadedShowcaseHandler)
.catch(console.error);
} catch(e){
console.error(e);
}
}
function loadedShowcaseHandler(mpSdk){
addTag()
function placeTag(){
if(tag) mpSdk.Mattertag.navigateToTag(tag, mpSdk.Mattertag.Transition.INSTANT);
tag = null;
}
window.addEventListener('blur',function(){
window.setTimeout(function () {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
window.focus()
addTag();
}
}, 0);
});
function updateTagPos(newPos, newNorm=undefined, scale=undefined){
if(!newPos) return;
if(!scale) scale = .33;
if(!newNorm) newNorm = {x: 0, y: 1, z: 0};
mpSdk.Mattertag.editPosition(tag, {
anchorPosition: newPos,
stemVector: {
x: scale * newNorm.x,
y: scale * newNorm.y,
z: scale * newNorm.z,
}
})
.catch(e =>{
console.error(e);
tag = null;
});
}
mpSdk.Pointer.intersection.subscribe(intersectionData => {
if(tag){
if(intersectionData.object === 'intersectedobject.model' || intersectionData.object === 'intersectedobject.sweep'){
updateTagPos(intersectionData.position, intersectionData.normal);
}
}
});
function addTag() {
if(!tag){
mpSdk.Mattertag.add([{
label: "Matterport Tag",
description: "",
anchorPosition: {x: 0, y: 0, z: 0},
stemVector: {x:0, y: 0, z: 0},
color: {r: 1, g: 0, b: 0},
}])
.then((sid) => {
tag = sid[0];
})
.catch( (e) => {
console.error(e);
})
}
}
} // loadedShowcaseHandler
The HTML source code:
<!DOCTYPE html>
<html>
<head>
<title>Transient Tag Editor</title>
<style>
#showcase {
width: 100%;
height: 100vh;
}
</style>
<script src="https://static.matterport.com/showcase-sdk/2.0.1-0-g64e7e88/sdk.js"></script>
<script src="/js/app-editor.js" type="text/javascript" defer></script>
</head>
<body>
<iframe id="showcase" frameborder="0" allowFullScreen allow="xr-spatial-tracking"></iframe>
</body>
</html>
It works!
Complete Code
The complete code for this sample and others is available on github:
github.com/loic-meister-guild/pj-matterport-sdk-2021-tutorial
See Also
Matterport SDK 2021 Tutorial
Node.js + Express Tutorial for 2021
How to detect a click event on a cross domain iframe
I am using tcpdf to generate a pdf. I only want the header to appear on the first page and have it so that it does not appear on the remaining pages. However, I would like the top margin of the remaining pages shifted up since there is no header on those pages.
I'm using MYPDF to extend TCPDF to customize the header to only show on the first page.
// Extend the TCPDF class to create custom Header and Footer
class MYPDF extends TCPDF {
//Page header
public function Header() {
if (count($this->pages) === 1) { // Do this only on the first page
$html .= 'header text';
}
$this->writeHTML($html, true, false, false, false, '');
}
}
// create new PDF document
$pdf = new MYPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, 'LETTER', true, 'UTF-8', false);
// set document information
$pdf->SetCreator(PDF_CREATOR);
// set default header data
$pdf->SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE, PDF_HEADER_STRING);
// set header and footer fonts
$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
// set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, 25, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(10);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
// set auto page breaks
$pdf->SetAutoPageBreak(TRUE, 20);
// set image scale factor
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
// set some language-dependent strings (optional)
if (#file_exists(dirname(__FILE__).'/lang/eng.php')) {
require_once(dirname(__FILE__).'/lang/eng.php');
$pdf->setLanguageArray($l);
}
// ---------------------------------------------------------
// set font
$pdf->SetFont('droidsansfallback', '', 10);
// add a page
$pdf->AddPage();
$pdf->resetColumns();
$pdf->setEqualColumns(3, 57); // KEY PART - number of cols and width
$pdf->selectColumn();
$content = '';
$content .= '
<table cellspacing="0" cellpadding="3">
';
$content .= fetch_data();
$content .= '</table>';
$pdf->writeHTML($content);
$pdf->Output('file.pdf', 'I');
Something like this worked for me:
public function Header() {
if ($this->page == 1) {
$html .= 'header text';
$this->writeHTML($html, true, false, false, false, '');
} else {
$this->SetMargins(PDF_MARGIN_LEFT, 10, PDF_MARGIN_RIGHT);
}
}
Only write the header HTML on the first page, then change the margins for the other pages.
Users upload images to this form by dropping a file on it.
Is there a way in to trigger drop with a File/Blob object programmatically? Something like MouseEvent.initMouseEvent() https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
but like initDropEvent?
I am researching here:
https://dxr.mozilla.org/mozilla-central/source/obj-x86_64-unknown-linux-gnu/_tests/testing/mochitest/mochijar/chrome/mochikit/content/tests/SimpleTest/ChromeUtils.js?offset=200#254
https://dxr.mozilla.org/mozilla-central/source/browser/base/content/test/newtab/head.js?offset=0#690
https://dxr.mozilla.org/mozilla-central/source/browser/base/content/test/newtab/browser_newtab_bug765628.js#25
https://dxr.mozilla.org/mozilla-central/source/testing/mochitest/tests/SimpleTest/ChromeUtils.js#179 ----- this one is a mochitest and code from mochitests never ever work for me, so i think this one is useless as it needs some special environment i think
Based on this I ran this code from scratchpad witha tab with twitter loaded, and aftre clicking the "tweet" button:
// https://dxr.mozilla.org/mozilla-central/source/browser/base/content/test/newtab/head.js?offset=100#690
//https://dxr.mozilla.org/mozilla-central/source/browser/base/content/test/newtab/browser_newtab_bug765628.js#21
/**
* Creates a custom drag event.
* #param aEventType The drag event's type.
* #param aData The event's drag data (optional).
* #return The drag event.
*/
function createDragEvent(aContentWindow, aEventType, aData, aDataType) {
// aDataType text/x-moz-url, text/plain, etc
let dataTransfer = new (aContentWindow).DataTransfer("dragstart", false);
dataTransfer.mozSetDataAt(aDataType, aData, 0);
let event = aContentWindow.document.createEvent("DragEvents");
event.initDragEvent(aEventType, true, true, aContentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
return event;
}
function sendTwitterDropEvent() {
var aContentWindow = gBrowser.contentWindow;
var aContentDocument = aContentWindow.document;
var btnNewTweet = aContentDocument.getElementById('global-new-tweet-button');
console.info('btnNewTweet:', btnNewTweet);
if (!btnNewTweet) {
throw new Error('global tweet button not found, probably not logged in');
}
btnNewTweet.click();
var inputAddPhoto = aContentDocument.getElementById('global-tweet-dialog').querySelector('input[type=file]');
if (!inputAddPhoto) {
throw new Error('add photo button not found! i have no idea what could cause this');
}
var formTweet = aContentDocument.getElementById('global-tweet-dialog-dialog').querySelector('form'); //<form.t1-form.tweet-form has-preview has-thumbnail dynamic-photos photo-square-4>
if (!formTweet) {
throw new Error('tweet form not found! i have no idea what could cause this');
}
console.info('formTweet:', (formTweet instanceof Ci.nsIDOMNode));
var ifaceReq = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor);
var windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
var aDragData = 'site 99';
var aDragDataType = 'plain/text';
var event = createDragEvent(aContentWindow, "drop", aDragData, aDragDataType);
windowUtils.dispatchDOMEventViaPresShell(formTweet, event, true);
}
sendTwitterDropEvent();
This should populate the tweet input with "site 99" but it doesnt. I was testing with plain text then after I get that working I was thinking of moving to File/Blob.
Simplified non-twitter test case
Open tab with this: data:text/html,<input id=rawr>
Open scratchpad and run this code from scratchpad:
function createDragEvent(aContentWindow, aEventType, aData, aDataType) {
// aDataType text/x-moz-url, text/plain, etc
let dataTransfer = new (aContentWindow).DataTransfer("dragstart", false);
dataTransfer.mozSetDataAt(aDataType, aData, 0);
let event = aContentWindow.document.createEvent("DragEvents");
event.initDragEvent(aEventType, true, true, aContentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
return event;
}
function sendTwitterDropEvent() {
var aContentWindow = gBrowser.contentWindow;
var aContentDocument = aContentWindow.document;
var formTweet = aContentDocument.getElementById('rawr');
if (!formTweet) {
throw new Error('tweet form not found! i have no idea what could cause this');
}
console.info('formTweet:', (formTweet instanceof Ci.nsIDOMNode), formTweet);
var ifaceReq = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor);
var windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
var aDragData = 'site 99';
var aDragDataType = 'plain/text';
var event = createDragEvent(aContentWindow, "drop", aDragData, aDragDataType);
var rezDrop = windowUtils.dispatchDOMEventViaPresShell(formTweet, event, true);
console.info('rezDrop:', rezDrop);
}
sendTwitterDropEvent();
I use this code for create a 80x30 mm pdf file with a 25x25 mm qrcode: I change the qrcode width and height but it doesn't resize and I always see a little qrcode into the page.
Where is the error?? Please help me... I can't undertand the problem! :)
<?php
require_once('../config/lang/eng.php');
require_once('../tcpdf.php');
// create new PDF document
$pdf = new TCPDF("L", "mm", array(80,30) , true, 'UTF-8', false);
//set margins
$pdf->SetMargins(0, PDF_MARGIN_TOP, 0);
$pdf->SetHeaderMargin(0);
$pdf->SetFooterMargin(0);
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
//set auto page breaks
$pdf->SetAutoPageBreak(false, 0);
//set image scale factor
$pdf->setImageScale(1);
//set some language-dependent strings
$pdf->setLanguageArray($l);
// add a page
$pdf->AddPage();
$pdf->SetAutoPageBreak(false, 0);
// new style
$style = array(
'border' => false,
'padding' => 'auto',
'fgcolor' => array(0,0,0),
'bgcolor' => false
);
$pdf->write2DBarcode('http://www.google.it/', 'QRCODE,H', 50, 1, 300, 300, $style, 'N');
// ---------------------------------------------------------
//Close and output PDF document
$pdf->Output('test.pdf', 'I');
//============================================================+
// END OF FILE
//============================================================+
?>
Thank you!!!
Problem solved:
Before:
[...]
$pdf = new TCPDF("L", "mm", array(80,30) , true, 'UTF-8', false);
[...]
$pdf->AddPage();
[/code]
After:
[code]
[...]
$pdf = new TCPDF("P", "mm", array(80,30) , true, 'UTF-8', false);
[...]
$pdf->AddPage('L', '', false, false);
[/code]
The problem is the constructor page orientation that create confusion to the system: default portrait is ok, I have only to change the Add page orientation to Landscape and the problem is solved.
Thank you again for the attention.
:)
I am getting tooltip on mouse hover by each row for current column but I am unable to get next column tooltip on continue hover on same row.
But I can get it if I hover on another row & again hover any column of the previous row by using:
listeners:{
'itemmouseenter': function (view, record, item, index, e, eOpts) {
var gridColums = view.getGridColumns();
var column = gridColums[e.getTarget(this.view.cellSelector).cellIndex];
Ext.fly(item).set({ 'data-qtip': 'Des:' + column.dataIndex });
}
}
Can anyone show me what I'm missing or point me in the right direction?
I have an easy one, using the renderer function:
{
xtype : 'gridcolumn',
dataIndex : 'status',
text : 'Status',
renderer : function(value, metadata) {
metadata.tdAttr = 'data-qtip="' + value + '"';
return value;
}
}
I was looking through this. I could manage to get the tool tip for each cell by doing something like this:
Ext.getCmp('DynamicDemandGrid').getView().on('render', function(view) {
view.tip = Ext.create('Ext.tip.ToolTip', {
// The overall target element.
target: view.el,
// Each grid row causes its own seperate show and hide.
delegate: view.cellSelector,
// Moving within the row should not hide the tip.
trackMouse: true,
// Render immediately so that tip.body can be referenced prior to the first show.
renderTo: Ext.getBody(),
listeners: {
// Change content dynamically depending on which element triggered the show.
beforeshow: function updateTipBody(tip) {
var gridColums = view.getGridColumns();
var column = gridColums[tip.triggerElement.cellIndex];
var val=view.getRecord(tip.triggerElement.parentNode).get(column.dataIndex);
tip.update(val);
}
}
});
});
Let me know if it helps
{
text: name,
width: 80,
dataIndex: dataIndex,
sortable: true,
listeners: {
afterrender: function ()
{
Ext.create('Ext.ToolTip',
{
target: this.getEl(),
anchor: direction | "top",
trackMouse: true,
html: this.text
});
}
}
}