How to place WT widgets into PDF? - c++

UPDATE
I have also made a post about this issue on the WT forums here http://redmine.webtoolkit.eu/issues/3042. For those of you looking for more information on styling, you will find more information there.
ORIGINAL POST
I have a problem with placing a WGroupBox (eventually a WTable) into a PDF. I am using the PDF sample code from the widget gallery, and have been trying to make it place a WTable I have into the PDF, that being unsuccessful I toned it back to a WGroupBox.
http://www.webtoolkit.eu/widgets/media/pdf-output
The "PDF Images" samples work fine.
The "Rendering HTML to PDF" is where I have problems.
I am using the example exactly as it appears on the page, with the only addition being a WContainerWidget pointer for the items that I want in the PDF.
I do not understand what I need to pass into the renderPdf function as the WString that will be passed into the WPdfRenderer render function, I have tried examining the html of the widget gallery code to compare with my own, and mimic what the gallery is doing, but everything has so far failed to make a PDF.
This is what the URL looks like after I press the "Create Pdf" button, it displays a blank page with an empty pdf "Loading" message at the bottom left.
http://127.0.0.1:8080/ui/report.pdf?wtd=Psx1WfLVpsvVDpxW&request=resource&resource=oylm6i6&rand=1
Sample code below.
//This is the code I have executing on my main page, just a group box with a few labels, a button for the sample pdf, a chart, and another button for the more advanced sample pdf(this one is broken).
//PDF Images sample/////////////////////////////////////////////////////////////////////////
Wt::WContainerWidget* container = table_container;
Wt::WResource* sample_pdf = new SamplePdfResource(container);
Wt::WPushButton* button1 = new Wt::WPushButton("Create pdf sample", container);
button1->setLink(sample_pdf);
//PDF Images sample chart///////////////////////////////////////////////////////////////////
Wt::Chart::WCartesianChart* chart = CreateCartesianChart(table_container);
Wt::WPainter* p = new Wt::WPainter((Wt::WPdfImage*)sample_pdf);
chart->paint(*p);
std::ofstream f("chart.pdf", std::ios::out | std::ios::binary);
sample_pdf->write(f);
//GroupBox to be placed into PDF////////////////////////////////////////////////////////////
Wt::WGroupBox* group_box = new Wt::WGroupBox("Example Group Box", table_container);
group_box->addStyleClass("example");
Wt::WText* text = new Wt::WText("This is a text sample within the group box 1", group_box);
text = new Wt::WText("This is a text sample within the group box 2", group_box);
text = new Wt::WText("This is a text sample within the group box 3", group_box);
//The pdf to take the GroupBox//////////////////////////////////////////////////////////////
Wt::WPushButton* button = new Wt::WPushButton("Create pdf", container);
Wt::WResource* pdf = new ReportResource(group_box, container);
button->setLink(pdf);
Below is the "Rendering HTML to PDF" code from the widget gallery, with the new container widget for a target.
namespace {
void HPDF_STDCALL error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no,
void *user_data) {
fprintf(stderr, "libharu error: error_no=%04X, detail_no=%d\n",
(unsigned int) error_no, (int) detail_no);
}
}
class ReportResource : public Wt::WResource
{
public:
ReportResource(Wt::WContainerWidget* target, Wt::WObject* parent = 0)
: Wt::WResource(parent),
_target(NULL)
{
suggestFileName("report.pdf");
_target = target;
}
virtual void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response)
{
response.setMimeType("application/pdf");
HPDF_Doc pdf = HPDF_New(error_handler, 0);
// Note: UTF-8 encoding (for TrueType fonts) is only available since libharu 2.3.0 !
HPDF_UseUTFEncodings(pdf);
renderReport(pdf);
HPDF_SaveToStream(pdf);
unsigned int size = HPDF_GetStreamSize(pdf);
HPDF_BYTE *buf = new HPDF_BYTE[size];
HPDF_ReadFromStream (pdf, buf, &size);
HPDF_Free(pdf);
response.out().write((char*)buf, size);
delete[] buf;
}
private:
Wt::WContainerWidget* _target;
void renderReport(HPDF_Doc pdf)
{
std::stringstream ss;
_target->htmlText(ss);
std::string out = ss.str();
std::string out_id = _target->id();
std::string out_parent_id = _target->parent()->id();
renderPdf(Wt::WString::tr(???), pdf);
}
void renderPdf(const Wt::WString& html, HPDF_Doc pdf)
{
HPDF_Page page = HPDF_AddPage(pdf);
HPDF_Page_SetSize(page, HPDF_PAGE_SIZE_A4, HPDF_PAGE_PORTRAIT);
Wt::Render::WPdfRenderer renderer(pdf, page);
renderer.setMargin(2.54);
renderer.setDpi(96);
renderer.render(html);
}
};
//PDF Images code
class SamplePdfResource : public Wt::WPdfImage
{
public:
SamplePdfResource(Wt::WObject *parent = 0)
: Wt::WPdfImage(400, 300, parent)
{
suggestFileName("line.pdf");
paint();
}
private:
void paint()
{
Wt::WPainter painter(this);
Wt::WPen thickPen;
thickPen.setWidth(5);
painter.setPen(thickPen);
painter.drawLine(50, 250, 150, 50);
painter.drawLine(150, 50, 250, 50);
painter.drawText(0, 0, 400, 300, Wt::AlignCenter | Wt::AlignTop, "Hello, PDF");
}
};
//Cartesian chart initialization
Wt::Chart::WCartesianChart* CreateCartesianChart(Wt::WContainerWidget* parent)
{
Wt::WStandardItemModel *model = new Wt::WStandardItemModel(PERFORMANCE_HISTORY, 2, parent);
//Create the scatter plot.
Wt::Chart::WCartesianChart* chart = new Wt::Chart::WCartesianChart(parent);
//Give the chart an empty model to fill with data
chart->setModel(model);
//Set which column holds X data
chart->setXSeriesColumn(0);
//Get the axes
Wt::Chart::WAxis& x_axis = chart->axis(Wt::Chart::Axis::XAxis);
Wt::Chart::WAxis& y1_axis = chart->axis(Wt::Chart::Axis::Y1Axis);
Wt::Chart::WAxis& y2_axis = chart->axis(Wt::Chart::Axis::Y2Axis);
//Modify axes attributes
x_axis.setRange(0, PERFORMANCE_HISTORY);
x_axis.setGridLinesEnabled(true);
x_axis.setLabelInterval(PERFORMANCE_HISTORY / 10);
y1_axis.setRange(0, 100);
y1_axis.setGridLinesEnabled(true);
y1_axis.setLabelInterval(10);
y2_axis.setRange(0, 100);
y2_axis.setVisible(true);
y2_axis.setLabelInterval(10);
//Set chart type
chart->setType(Wt::Chart::ChartType::ScatterPlot);
// Typically, for mathematical functions, you want the axes to cross at the 0 mark:
chart->axis(Wt::Chart::Axis::XAxis).setLocation(Wt::Chart::AxisValue::ZeroValue);
chart->axis(Wt::Chart::Axis::Y1Axis).setLocation(Wt::Chart::AxisValue::ZeroValue);
chart->axis(Wt::Chart::Axis::Y2Axis).setLocation(Wt::Chart::AxisValue::ZeroValue);
// Add the lines
Wt::Chart::WDataSeries s(1, Wt::Chart::SeriesType::LineSeries);
chart->addSeries(s);
//Size the display size of the chart, has no effect on scale
chart->resize(300, 300);
return chart;
}

The pdf renderer takes a HTML string. So I think the solution is
renderPdf(WString::fromUTF8(out), pdf);

Related

Why QTextDocument background color changes only once?

I am using TextEditor from the example provided with Qt (https://doc.qt.io/qt-5/qtquickcontrols1-texteditor-example.html). Here is the complete code https://code.qt.io/cgit/qt/qtquickcontrols.git/tree/examples/quickcontrols/controls/texteditor?h=5.15
I have created a custom method void DocumentHandler::setBackgroundColor(const QColor &color) to change the background color of the overall HTML document.
My problem is whenever I call a method to change the background color of QTextDocument using setDefaultStyleSheet, it is executed only once. ie, my document background changes only once. For the next calls, I can see the qDebug printing correctly, but the setDefaultStyleSheet doesn't work. However, everything works perfectly with normal text, only not with m_doc->toHtml().
How do I fix this?
If I change m_doc->setHtml("some random text"), it works as required
...
QColor DocumentHandler::backgroundColor() const
{
return m_backgroundColor;
}
void DocumentHandler::setBackgroundColor(const QColor &color)
{
m_backgroundColor = color.name(); // QColor variable
m_doc->setDefaultStyleSheet("body{background-color: '"+ color.name() +"'}"); // m_doc is QTextDocument *
m_doc->setHtml(m_doc->toHtml());
qDebug() << "BACKGROUND COLOR CHANGED" << color.name() ;
emit backgroundColorChanged();
}
In the QML, I have called it like this
...
DocumentHandlerModel {
id: document
target: textArea
cursorPosition: textArea.cursorPosition
selectionStart: textArea.selectionStart
selectionEnd: textArea.selectionEnd
backgroundColor: colorDialog2.color
onFontFamilyChanged: {
var index = Qt.fontFamilies().indexOf(document.fontFamily)
if (index === -1) {
fontFamilyComboBox.currentIndex = 0
fontFamilyComboBox.special = true
} else {
fontFamilyComboBox.currentIndex = index
fontFamilyComboBox.special = false
}
}
onError: {
errorDialog.text = message
errorDialog.visible = true
}
}
Cause
The documentation of QTextDocument::setDefaultStyleSheet says:
Note: Changing the default style sheet does not have any effect to the existing content of the document.
You try to overcome this by calling setHtml after setDefaultStyleSheet like that:
m_doc->setHtml(m_doc->toHtml());
However, this does not produce the desired result, because setDefaultStyleSheet actually embeds the background color in the HTML through the bgcolor CSS property.
To test this, add
m_doc->setHtml("<html><body><p>Test</p></body></html>");
qDebug() << m_doc->toHtml();
after
m_doc->setHtml(m_doc->toHtml());
The HTML content of m_doc from <html><body><p>Test</p></body></html>, when tested with #FF00FF, becames:
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;\" bgcolor=\"#ff00ff\">\n<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Test</p></body></html>"
Note: Please, notice that assignment: bgcolor=\"#ff00ff\.
So, by writing m_doc->setHtml(m_doc->toHtml()); you effectively return the old color. That is why the color changes only the first time.
By the way, the widget now changes its color, but has lost its content.
Solution
It is hard to find an elegant solution, because in this case markup and styles are not kept separate, as in real HTML and CSS, but the style is embedded by Qt in the markup. The one thing which comes to my mind, is to parse the content of m_doc manually.
Note: Such a solution would be extremely fragile and I advise strongly against it. Maybe instead of using a stylesheet, change the background color by setting up the palette of the widget, which renders the content of the QTextDocument on the screen.
Note: In any case this does not seem like the intended behavior, at least it does not match the description in the documentation, so I would have reported it as a bug to Qt, if I were you.
Example
Here is an example I wrote for you in order to demonstrate the proposed solution:
void DocumentHandler::setBackgroundColor(const QColor &color)
{
const QString &bgcolor("bgcolor=\"");
QString html(m_doc->toHtml());
int n = html.indexOf(bgcolor, html.indexOf("<body"));
int k = n + bgcolor.length();
m_doc->setDefaultStyleSheet("body { background-color: '" + color.name() + "' }");
if (n >= 0)
html.replace(n + bgcolor.length(), html.mid(k, html.indexOf("\"", n + bgcolor.length()) - k).length(), color.name());
m_doc->setHtml(html);
m_backgroundColor = color.name(); // QColor variable
emit backgroundColorChanged();
}

Filename of image in event box

I'm pretty new to GTK3.
I've created an array of EventBoxes and each of them contains an image. All boxes are linked to the same on_button_pressed method. When the user clicks an image, I'd like to get the file name of the image displayed in the event box. Is there any way this can be done?
Thanks for all the help.
---- EDIT 1 -------------------------
Code:
//In my header file, I've declared:
class Window : public Gtk::Window{
//....some other window elements....
Gtk::EventBox portraits[13];
Gtk::Image * portraitImage[13];
};
//In my .cc file, I've implemented:
Window::Window(){
//....creating some window elements ....
for(int i = 0; i < 13; i++){
portraitImage[i] = new Gtk::Image("empty.png");
portraits[i].signal_button_press_event().connect(sigc::mem_fun(*this,
&Window::doOnPortraitClicked));
portraits[i].set_image(*portraitImage[i]);
}
//..........some other code for displaying the window..........//
show_all_children()
}
//doOnPortraitClicked method
bool Window::doOnPortraitClicked(GdkEventButton * button){
?? GValue value = G_VALUE_INIT;
?? g_value_init (&value, G_TYPE_STRING);
?? g_object_get_property(G_OBJECT(button->get_image()), "file", &value);
?? string source = g_value_get_string(&value);
// How do I retrieve "empty.png" from the EventButton?
}
The question marks are lines I can't implement

captureScreen in cocos-2d C++ - how to use or save the captured image?

I want to have a button in my (iOS) app that takes a screenshot of the current screen and then attaches it to a text message.
Like I have seen in this other app...
I have message sending working, and I think I have screenshot working, but I don't know where the screenshot is saved or how to use it.
My message sending is called from a button in the app...
void GameOverScene::messageCallBack(cocos2d::Ref *sender) {
CocosDenshion::SimpleAudioEngine::getInstance()->playEffect(ALL_BUTTONS_CLICK_SOUND_NAME);
utils::captureScreen( CC_CALLBACK_2(GameOverScene::afterCaptured, this), "screenshot.png" );
__String *messageTextOne = __String::create("sms:&body=Hey,%20I%20just%20hit%20a%20score%20of%20");
__String *messageTextTwo = __String::createWithFormat("%i", score);
__String *messageURL = __String::createWithFormat("%s%s", messageTextOne->getCString(), messageTextTwo->getCString());
Application::getInstance()->openURL(messageURL->getCString());
}
and the screenshot function is...
void GameOverScene::afterCaptured( bool succeed, const std::string &outputFile ) {
if (succeed) {
log("Screen capture succeeded");
Size screenSize = Director::getInstance()->getWinSize();
RenderTexture * tex = RenderTexture::create(screenSize.width, screenSize.height);
tex->setPosition(screenSize.width/2, screenSize.height/2);
tex->begin();
this->getParent()->visit();
tex->end();
tex->saveToFile("Image_Save.png", Image::Format::PNG);
} else {
log("Screen capture failed");
}
}
I get the message in the console "Screen capture succeeded", and my message app opens up with the prefilled text message.
What I need to do is to add the screenshot to this message, but I cant see how to do that, or where the screenshot is saved, or how to use the saved screenshot.
If it succeeded it's saved in private application directory. Use software like iExplorer, find your app and it should be inside Documents directory. If you want to see it in Photos you have manually add it there.
In order to achieve this, you have to call
UIImageWriteToSavedPhotosAlbum
see: How to save picture to iPhone photo library?
You have to create a function in AppController.m and use it from here. You can call objc code from c++. It requires UIImage so first you have to pass filepath and load uiimage from it and then finally add to gallery.
You can use something like this to get the path of image saved:
void CustomLayerColor::saveFile(Node*node, std::string fileName, const renderTextureCallback& callBack) {
float renderTextureWidth = node->getBoundingBox().size.width;
float renderTextureHeight = node->getBoundingBox().size.height;
RenderTexture * renderTexture = RenderTexture::create(renderTextureWidth,renderTextureHeight);
renderTexture->begin();
node->visit();
renderTexture->end();
renderTexture->saveToFile(fileName, Image::Format::PNG, true,callBack);
}
void CustomLayerColor::onSaveFileCallBack(RenderTexture * mRenderTexture, const std::string& savedFileNameWithFullPath) {
}

QMarginsF() not setting margins in QWebEnginePage::printToPdf

I'd like to know if there's anyone who can help me set some margins into qt QWebEnginePage::printToPdf pdf.
I'm loading an html from some url and rendering it with QWebEnginePage and then using printToPdf method to create the pdf file from html content.
But i'm not being able to set document margins (i've managed to set those margins with css, but i'd like to set it with the propert Qt class (QMarginsF)).
The code:
// NEW QT WEBENGINE PAGE
QWebEnginePage page;
// MARGINS
QMarginsF margins(75,75,75,75);
// CONNECT
QObject::connect( &page, &QWebEnginePage::loadFinished, [&page, output, &margins]()
{
// qDebug(QString("Margins: %1").arg(margins.top()).toStdString().c_str());
// PRINT TO PDF
page.printToPdf( [output](const QByteArray &data)
// LAMBDA (print2pdf first param)
{
QFile file(output);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
{
return;
}
file.write( data );
system( (" xdg-open " + output).toStdString().c_str() );
QApplication::exit(0);
},
// PAGE LAYOUT (print2pdf second param)
QPageLayout( QPageSize( QPageSize::A4 ), QPageLayout::Portrait, margins));
});
Thanks in advance.

GTKmm - How to put a pixbuf in a treeview

I'm learning how to use GTKmm and I'm having a really hard time figuring out how to put an image into a treeview. I used Glade to create a treestore with 3 columns, one of which is a GdkPixbuf called store_pixbuf. I also created a treeview in glade, with a column that has both a pixbuf cell renderer called int_col_pict and a char array cell renderer. In my code, I have the usual MyColumns definition for the treestore like:
class MyModelColumns : public Gtk::TreeModel::ColumnRecord
{
public:
Gtk::TreeModelColumn<Glib::ustring> store_hostname;
Gtk::TreeModelColumn<Glib::ustring> store_intname;
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > store_pict;
MyModelColumns () { add(store_hostname); add(store_intname); add(store_pict);}
};
and use the following bit of code to populate it.
//Get a pointer to the treestore
Glib::RefPtr<Gtk::TreeStore> treestore = Glib::RefPtr<Gtk::TreeStore>::cast_static(builder->get_object("routerTreeStore"));
//make sure the pointer isn't bad
if(treestore){
MyModelColumns columns;
//populate the first column
Gtk::TreeRow row= *(treestore->append());
row[columns.store_hostname] = router->hostname;
//populate all children
for(int i=0; i<router->interfaces.size(); i++)
{
//append child row
Gtk::TreeRow child = *(treestore->append(row.children()));
//insert data into the row
child[columns.store_pict] = Gdk::Pixbuf::create_from_file("red_dot.png");
child[columns.store_intname] = router->interfaces[i].interfaceName;
}
}//if
I initially tried to use a stock image, but I could not figure out what function I was supposed to use, so then I tried to use Gdk::Pixbuf::create_from_file() (as you can see above), but at run time I get the following error:
Gtk-WARNING **: gtktreestore.c:765: Unable to convert from GdkPixbuf to gtkmm__GdkPixbuf
Click here to see what it looks like running. The image is supposed to go on the same line as the "FastEthernet..." lines
Does anyone know how I can solve this? Am I going about it completely wrong? Thanks for looking, every little bit of help is appreciated!
Your code looks correct. I just coded up a quick example and with gtkmm-2.4, I have no problems creating a column for a Glib::RefPtr
I couple of questions: what version of gtkmm are you using? Are you adding a column in the treeview for the Pixbuf?
I won't post my complete example but the relevant bits are:
in example.h
//Tree model columns:
class ModelColumns : public Gtk::TreeModel::ColumnRecord
{
public:
ModelColumns()
{ add(m_col_store_pict);}
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > m_col_store_pict;
};
ModelColumns m_Columns;
in example.cpp
//Create the Tree model:
m_refTreeModel = Gtk::ListStore::create(m_Columns);
m_TreeView.set_model(m_refTreeModel);
//Fill the TreeView's model
Gtk::TreeModel::Row row = *(m_refTreeModel->append());
row[m_Columns.m_col_store_pict] = Gdk::Pixbuf::create_from_file("/usr/share/icons/gnome/22x22/apps/arts.png");
row = *(m_refTreeModel->append());
row[m_Columns.m_col_store_pict] = Gdk::Pixbuf::create_from_file("/usr/share/icons/gnome/22x22/apps/fonts.png");
row = *(m_refTreeModel->append());
row[m_Columns.m_col_store_pict] = Gdk::Pixbuf::create_from_file("/usr/share/icons/gnome/22x22/apps/access.png");
//Add the TreeView's view columns:
m_TreeView.append_column("Some Picture", m_Columns.m_col_store_pict);
Is that any help?
If all you want is a potentially different stock icon for each row, you can do it by setting an pixbuf renderer attribute. The idea is that the column contains a string stock-id, which the renderer displays as an icon.
// in your class declaration
struct columnsRecord : public Gtk::TreeModel::ColumnRecord {
...
Gtk::TreeModelColumn<std::string> stockID; // stock id name
...
} treeColumns;
Gtk::CellRendererPixBuf pixBufRenderer;
...
// setting up columns
int numcols = treeView.append_column("Icon Column", pixBufRenderer); // returns # cols after append
treeView.get_column(numcols-1)->add_attribute(pixBufRenderer,"stock-id",treeColumns.stockID)
...
// setting a row
std::string id = good_val ? Gtk::Stock::YES.id : Gtk::Stock::NO.id;
rowiter->set_value(columns.stockID,id);
Looks like it's necessary to explicitly set CellRenderer for pixbuf cells.
This code snippet displays CANCEL icon on each data row.
Gtk::CellRendererPixbuf *cross = Gtk::manage(new Gtk::CellRendererPixbuf());
cross->property_stock_id() = Gtk::StockID(Gtk::Stock::CANCEL).get_string();
cross->property_stock_size() = Gtk::ICON_SIZE_BUTTON;
[...]
int cols_count = m_TreeView.append_column("icons", *cross);
For displaying custom images you need to remove two lines setting stock_id add something like this below:
Gtk::TreeViewColumn* pColumn = m_TreeView.get_column(cols_count - 1);
if(pColumn) {
pColumn->add_attribute(cell->property_value(), columns.store_pict);
}