You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/utilities/imageeditor/tools/imageprint.cpp

815 lines
23 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2004-07-13
* Description : image editor printing interface.
*
* Copyright (C) 2006 by F.J. Cruz <fj.cruz@supercable.es>
* Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
*
* KeepRatio and Alignment options imported from Gwenview program.
* Copyright (c) 2003 Angelo Naselli <anaselli at linux dot it>
*
* Original printing code from Kuickshow program.
* Copyright (C) 2002 Carsten Pfeiffer <pfeiffer at kde.org>
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* ============================================================ */
// TQt includes.
#include <tqobject.h>
#include <tqpixmap.h>
#include <tqlayout.h>
#include <tqgroupbox.h>
#include <tqbuttongroup.h>
#include <tqstring.h>
#include <tqsize.h>
#include <tqcursor.h>
#include <tqlabel.h>
#include <tqhbox.h>
#include <tqcheckbox.h>
#include <tqfont.h>
#include <tqgrid.h>
#include <tqimage.h>
#include <tqpaintdevicemetrics.h>
#include <tqpainter.h>
#include <tqradiobutton.h>
#include <tqvbuttongroup.h>
#include <tqcolor.h>
#include <tqcombobox.h>
#include <tqstyle.h>
#include <tqpushbutton.h>
// KDE includes.
#include <klocale.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kapplication.h>
#include <tdeconfig.h>
#include <kimageio.h>
#include <kcombobox.h>
#include <kglobalsettings.h>
#include <knuminput.h>
#include <kprinter.h>
#include <ktempfile.h>
#include <kpropertiesdialog.h>
// Local includes.
#include "ddebug.h"
#include "dimg.h"
#include "editorwindow.h"
#include "icctransform.h"
#include "imageprint.h"
#include "imageprint.moc"
namespace Digikam
{
class ImagePrintPrivate
{
public:
ImagePrintPrivate(){}
TQString filename;
TQString inProfilePath;
TQString outputProfilePath;
DImg image;
};
ImagePrint::ImagePrint(DImg& image, KPrinter& printer, const TQString& filename)
: m_printer(printer)
{
d = new ImagePrintPrivate();
d->image = image;
d->filename = filename;
}
ImagePrint::~ImagePrint()
{
delete d;
}
bool ImagePrint::printImageWithTQt()
{
if ( d->image.isNull() )
{
DWarning() << "Supplied Image for printing is null" << endl;
return false;
}
TQString t = "true";
TQString f = "false";
if (m_printer.option( "app-imageeditor-color-managed") != f)
{
IccTransform *transform = new IccTransform();
readSettings();
if (d->image.getICCProfil().isNull())
{
transform->setProfiles( d->inProfilePath, d->outputProfilePath );
}
else
{
transform->setProfiles(d->outputProfilePath);
}
transform->apply( d->image );
}
TQImage image2Print = d->image.copyTQImage();
// Black & white print ?
if ( m_printer.option( "app-imageeditor-blackwhite" ) != f)
{
image2Print = image2Print.convertDepth( 1, Qt::MonoOnly |
Qt::ThresholdDither |
Qt::AvoidDither );
}
TQPainter p;
p.begin( &m_printer );
TQPaintDeviceMetrics metrics( &m_printer );
p.setFont( TDEGlobalSettings::generalFont() );
TQFontMetrics fm = p.fontMetrics();
int w, h; // will be set to the width and height of the printer
// when the orientation is decided.
w = metrics.width();
h = metrics.height();
int filenameOffset = 0;
TQSize size = image2Print.size();
bool printFilename = m_printer.option( "app-imageeditor-printFilename" ) != f;
if ( printFilename )
{
// filename goes into one line!
filenameOffset = fm.lineSpacing() + 14;
h -= filenameOffset;
}
if ( m_printer.option( "app-imageeditor-scaleToFit" ) != f )
{
if ( m_printer.option( "app-imageeditor-auto-rotate" ) == t )
m_printer.setOrientation( size.width() <= size.height() ? KPrinter::Portrait
: KPrinter::Landscape );
// Scale image to fit pagesize
size.scale( w, h, TQSize::ScaleMin );
}
else
{
int unit = (m_printer.option("app-imageeditor-scale-unit").isEmpty() ?
ImageEditorPrintDialogPage::DK_INCHES : m_printer.option("app-imageeditor-scale-unit").toInt());
double inches = 1;
if (unit == ImageEditorPrintDialogPage::DK_MILLIMETERS)
{
inches = 1/25.4;
}
else if (unit == ImageEditorPrintDialogPage::DK_CENTIMETERS)
{
inches = 1/2.54;
}
double wImg = (m_printer.option("app-imageeditor-scale-width").isEmpty() ?
1 : m_printer.option("app-imageeditor-scale-width").toDouble()) * inches;
double hImg = (m_printer.option("app-imageeditor-scale-height").isEmpty() ?
1 : m_printer.option("app-imageeditor-scale-height").toDouble()) * inches;
size.setWidth( int(wImg * m_printer.resolution()) );
size.setHeight( int(hImg * m_printer.resolution()) );
if ( m_printer.option( "app-imageeditor-auto-rotate" ) == t )
m_printer.setOrientation( wImg <= hImg ? KPrinter::Portrait : KPrinter::Landscape );
if (size.width() > w || size.height() > h)
{
int resp = KMessageBox::warningYesNoCancel(TDEApplication::kApplication()->mainWidget(),
i18n("The image will not fit on the page, what do you want to do?"),
TQString(),KStdGuiItem::cont(),
i18n("Shrink") );
if (resp==KMessageBox::Cancel)
{
m_printer.abort();
// no need to return false, user decided to abort
return true;
}
else if (resp == KMessageBox::No)
{ // Shrink
size.scale(w, h, TQSize::ScaleMin);
}
}
}
// Align image.
int alignment = (m_printer.option("app-imageeditor-alignment").isEmpty() ?
TQt::AlignCenter : m_printer.option("app-imageeditor-alignment").toInt());
int x = 0;
int y = 0;
// x - alignment
if ( alignment & TQt::AlignHCenter )
x = (w - size.width())/2;
else if ( alignment & TQt::AlignLeft )
x = 0;
else if ( alignment & TQt::AlignRight )
x = w - size.width();
// y - alignment
if ( alignment & TQt::AlignVCenter )
y = (h - size.height())/2;
else if ( alignment & TQt::AlignTop )
y = 0;
else if ( alignment & TQt::AlignBottom )
y = h - size.height();
// Perform the actual drawing.
p.drawImage( TQRect( x, y, size.width(), size.height()), image2Print );
if ( printFilename )
{
TQString fname = minimizeString( d->filename, fm, w );
if ( !fname.isEmpty() )
{
int fw = fm.width( fname );
int x = (w - fw)/2;
int y = metrics.height() - filenameOffset/2;
p.drawText( x, y, fname );
}
}
p.end();
return true;
}
TQString ImagePrint::minimizeString( TQString text, const TQFontMetrics& metrics,
int maxWidth )
{
// no sense to cut that tiny little string
if ( text.length() <= 5 )
return TQString();
bool changed = false;
while ( metrics.width( text ) > maxWidth )
{
int mid = text.length() / 2;
// remove 2 characters in the middle
text.remove( mid, 2 );
changed = true;
}
// add "..." in the middle
if ( changed )
{
int mid = text.length() / 2;
// sanity check
if ( mid <= 5 )
return TQString();
text.replace( mid - 1, 3, "..." );
}
return text;
}
void ImagePrint::readSettings()
{
TDEConfig* config = kapp->config();
config->setGroup("Color Management");
d->inProfilePath = config->readPathEntry("WorkSpaceProfile");
d->outputProfilePath = config->readPathEntry("ProofProfileFile");
}
// Image print dialog class -------------------------------------------------------------
class ImageEditorPrintDialogPagePrivate
{
public:
ImageEditorPrintDialogPagePrivate()
{
cmEnabled = false;
position = 0;
keepRatio = 0;
scaleToFit = 0;
scale = 0;
addFileName = 0;
blackwhite = 0;
autoRotate = 0;
colorManaged = 0;
cmPreferences = 0;
parent = 0;
width = 0;
height = 0;
units = 0;
}
bool cmEnabled;
TQRadioButton *scaleToFit;
TQRadioButton *scale;
TQCheckBox *keepRatio;
TQCheckBox *addFileName;
TQCheckBox *blackwhite;
TQCheckBox *autoRotate;
TQCheckBox *colorManaged;
TQPushButton *cmPreferences;
TQWidget *parent;
KDoubleNumInput *width;
KDoubleNumInput *height;
KComboBox *position;
KComboBox *units;
DImg image;
ImageEditorPrintDialogPage::Unit previousUnit;
};
ImageEditorPrintDialogPage::ImageEditorPrintDialogPage(DImg& image, TQWidget *parent, const char *name )
: KPrintDialogPage( parent, name )
{
d = new ImageEditorPrintDialogPagePrivate;
d->image = image;
d->parent = parent;
setTitle( i18n("Image Settings") );
readSettings();
TQVBoxLayout *layout = new TQVBoxLayout( this );
layout->setMargin( KDialog::marginHint() );
layout->setSpacing( KDialog::spacingHint() );
// ------------------------------------------------------------------------
TQHBoxLayout *layout2 = new TQHBoxLayout( layout );
layout2->setSpacing(3);
TQLabel* textLabel = new TQLabel( this, "Image position:" );
textLabel->setText( i18n( "Image position:" ) );
layout2->addWidget( textLabel );
d->position = new KComboBox( false, this, "Print position" );
d->position->clear();
d->position->insertItem( i18n( "Top-Left" ) );
d->position->insertItem( i18n( "Top-Central" ) );
d->position->insertItem( i18n( "Top-Right" ) );
d->position->insertItem( i18n( "Central-Left" ) );
d->position->insertItem( i18n( "Central" ) );
d->position->insertItem( i18n( "Central-Right" ) );
d->position->insertItem( i18n( "Bottom-Left" ) );
d->position->insertItem( i18n( "Bottom-Central" ) );
d->position->insertItem( i18n( "Bottom-Right" ) );
layout2->addWidget( d->position );
TQSpacerItem *spacer1 = new TQSpacerItem( 101, 21, TQSizePolicy::Expanding, TQSizePolicy::Minimum );
layout2->addItem( spacer1 );
d->addFileName = new TQCheckBox( i18n("Print fi&lename below image"), this);
d->addFileName->setChecked( false );
layout->addWidget( d->addFileName );
d->blackwhite = new TQCheckBox ( i18n("Print image in &black and white"), this);
d->blackwhite->setChecked( false );
layout->addWidget (d->blackwhite );
d->autoRotate = new TQCheckBox( i18n("&Auto-rotate page"), this );
d->autoRotate->setChecked( false );
layout->addWidget( d->autoRotate );
// ------------------------------------------------------------------------
TQHBox *cmbox = new TQHBox(this);
d->colorManaged = new TQCheckBox(i18n("Use Color Management for Printing"), cmbox);
d->colorManaged->setChecked( false );
d->cmPreferences = new TQPushButton(i18n("Settings..."), cmbox);
TQWidget *space = new TQWidget(cmbox);
cmbox->setStretchFactor(space, 10);
cmbox->setSpacing(KDialog::spacingHint());
layout->addWidget(cmbox);
// ------------------------------------------------------------------------
TQVButtonGroup *group = new TQVButtonGroup( i18n("Scaling"), this );
group->setRadioButtonExclusive( true );
layout->addWidget( group );
d->scaleToFit = new TQRadioButton( i18n("Scale image to &fit"), group );
d->scaleToFit->setChecked( true );
d->scale = new TQRadioButton( i18n("Print e&xact size: "), group );
TQHBox *hb = new TQHBox( group );
hb->setSpacing( KDialog::spacingHint() );
TQWidget *w = new TQWidget(hb);
w->setFixedWidth(d->scale->style().subRect( TQStyle::SR_RadioButtonIndicator, d->scale ).width());
d->width = new KDoubleNumInput( hb, "exact width" );
d->width->setMinValue( 1 );
new TQLabel( "x", hb );
d->height = new KDoubleNumInput( hb, "exact height" );
d->height->setMinValue( 1 );
d->units = new KComboBox( false, hb, "unit combobox" );
d->units->insertItem( i18n("Millimeters") );
d->units->insertItem( i18n("Centimeters") );
d->units->insertItem( i18n("Inches") );
d->keepRatio = new TQCheckBox( i18n("Keep ratio"), hb);
w = new TQWidget(hb);
hb->setStretchFactor( w, 1 );
d->previousUnit = DK_MILLIMETERS;
// ------------------------------------------------------------------------
connect( d->colorManaged, TQT_SIGNAL(toggled(bool)),
this, TQT_SLOT(slotAlertSettings( bool )) );
connect( d->cmPreferences, TQT_SIGNAL(clicked()),
this, TQT_SLOT(slotSetupDlg()) );
connect( d->scale, TQT_SIGNAL( toggled( bool )),
this, TQT_SLOT( toggleScaling( bool )));
connect(d->width, TQT_SIGNAL( valueChanged( double )),
this, TQT_SLOT( slotWidthChanged( double )));
connect(d->height, TQT_SIGNAL( valueChanged( double )),
this, TQT_SLOT( slotHeightChanged( double )));
connect(d->keepRatio, TQT_SIGNAL( toggled( bool )),
this, TQT_SLOT( toggleRatio( bool )));
connect(d->units, TQT_SIGNAL(activated(const TQString &)),
this, TQT_SLOT(slotUnitChanged(const TQString& )));
}
ImageEditorPrintDialogPage::~ImageEditorPrintDialogPage()
{
delete d;
}
void ImageEditorPrintDialogPage::getOptions( TQMap<TQString,TQString>& opts, bool /*incldef*/ )
{
TQString t = "true";
TQString f = "false";
opts["app-imageeditor-alignment"] = TQString::number(getPosition(d->position->currentText()));
opts["app-imageeditor-printFilename"] = d->addFileName->isChecked() ? t : f;
opts["app-imageeditor-blackwhite"] = d->blackwhite->isChecked() ? t : f;
opts["app-imageeditor-scaleToFit"] = d->scaleToFit->isChecked() ? t : f;
opts["app-imageeditor-scale"] = d->scale->isChecked() ? t : f;
opts["app-imageeditor-scale-unit"] = TQString::number(stringToUnit(d->units->currentText()));
opts["app-imageeditor-scale-width"] = TQString::number( d->width->value() );
opts["app-imageeditor-scale-height"] = TQString::number( d->height->value() );
opts["app-imageeditor-scale-KeepRatio"] = d->keepRatio->isChecked() ? t : f;
opts["app-imageeditor-auto-rotate"] = d->autoRotate->isChecked() ? t : f;
opts["app-imageeditor-color-managed"] = d->colorManaged->isChecked() ? t : f;
}
void ImageEditorPrintDialogPage::setOptions( const TQMap<TQString,TQString>& opts )
{
TQString t = "true";
TQString f = "false";
TQString stVal;
bool ok;
double dVal;
int iVal;
iVal = opts["app-imageeditor-alignment"].toInt( &ok );
if (ok)
{
stVal = setPosition(iVal);
d->position->setCurrentItem(stVal);
}
d->addFileName->setChecked( opts["app-imageeditor-printFilename"] != f );
// This sound strange, but if I copy the code on the line above, the checkbox
// was always checked. And this is not the wanted behavior. So, with this works.
// KPrint magic ;-)
d->blackwhite->setChecked ( false );
d->scaleToFit->setChecked( opts["app-imageeditor-scaleToFit"] != f );
d->scale->setChecked( opts["app-imageeditor-scale"] == t );
d->autoRotate->setChecked( opts["app-imageeditor-auto-rotate"] == t );
d->colorManaged->setChecked( false );
Unit unit = static_cast<Unit>( opts["app-imageeditor-scale-unit"].toInt( &ok ) );
if (ok)
{
stVal = unitToString(unit);
d->units->setCurrentItem(stVal);
d->previousUnit = unit;
}
else
{
//for back compatibility
d->units->setCurrentItem(i18n("Millimeters"));
}
dVal = opts["app-imageeditor-scale-width"].toDouble( &ok );
if ( ok )
d->width->setValue( dVal );
dVal = opts["app-imageeditor-scale-height"].toDouble( &ok );
if ( ok )
d->height->setValue( dVal );
if ( d->scale->isChecked() == d->scaleToFit->isChecked() )
d->scaleToFit->setChecked( !d->scale->isChecked() );
d->keepRatio->setChecked( opts["app-imageeditor-scale-KeepRatio"] == t );
}
int ImageEditorPrintDialogPage::getPosition(const TQString& align)
{
int alignment;
if (align == i18n("Central-Left"))
{
alignment = TQt::AlignLeft | TQt::AlignVCenter;
}
else if (align == i18n("Central-Right"))
{
alignment = TQt::AlignRight | TQt::AlignVCenter;
}
else if (align == i18n("Top-Left"))
{
alignment = TQt::AlignTop | TQt::AlignLeft;
}
else if (align == i18n("Top-Right"))
{
alignment = TQt::AlignTop | TQt::AlignRight;
}
else if (align == i18n("Bottom-Left"))
{
alignment = TQt::AlignBottom | TQt::AlignLeft;
}
else if (align == i18n("Bottom-Right"))
{
alignment = TQt::AlignBottom | TQt::AlignRight;
}
else if (align == i18n("Top-Central"))
{
alignment = TQt::AlignTop | TQt::AlignHCenter;
}
else if (align == i18n("Bottom-Central"))
{
alignment = TQt::AlignBottom | TQt::AlignHCenter;
}
else
{
// Central
alignment = TQt::AlignCenter; // TQt::AlignHCenter || TQt::AlignVCenter
}
return alignment;
}
TQString ImageEditorPrintDialogPage::setPosition(int align)
{
TQString alignment;
if (align == (TQt::AlignLeft | TQt::AlignVCenter))
{
alignment = i18n("Central-Left");
}
else if (align == (TQt::AlignRight | TQt::AlignVCenter))
{
alignment = i18n("Central-Right");
}
else if (align == (TQt::AlignTop | TQt::AlignLeft))
{
alignment = i18n("Top-Left");
}
else if (align == (TQt::AlignTop | TQt::AlignRight))
{
alignment = i18n("Top-Right");
}
else if (align == (TQt::AlignBottom | TQt::AlignLeft))
{
alignment = i18n("Bottom-Left");
}
else if (align == (TQt::AlignBottom | TQt::AlignRight))
{
alignment = i18n("Bottom-Right");
}
else if (align == (TQt::AlignTop | TQt::AlignHCenter))
{
alignment = i18n("Top-Central");
}
else if (align == (TQt::AlignBottom | TQt::AlignHCenter))
{
alignment = i18n("Bottom-Central");
}
else
{
// Central: TQt::AlignCenter or (TQt::AlignHCenter || TQt::AlignVCenter)
alignment = i18n("Central");
}
return alignment;
}
void ImageEditorPrintDialogPage::toggleScaling( bool enable )
{
d->width->setEnabled( enable );
d->height->setEnabled( enable );
d->units->setEnabled( enable );
d->keepRatio->setEnabled( enable );
}
void ImageEditorPrintDialogPage::slotHeightChanged (double value)
{
d->width->blockSignals(true);
d->height->blockSignals(true);
if (d->keepRatio->isChecked())
{
double width = (d->image.width() * value) / d->image.height();
d->width->setValue( width ? width : 1.);
}
d->height->setValue(value);
d->width->blockSignals(false);
d->height->blockSignals(false);
}
void ImageEditorPrintDialogPage::slotWidthChanged (double value)
{
d->width->blockSignals(true);
d->height->blockSignals(true);
if (d->keepRatio->isChecked())
{
double height = (d->image.height() * value) / d->image.width();
d->height->setValue( height ? height : 1);
}
d->width->setValue(value);
d->width->blockSignals(false);
d->height->blockSignals(false);
}
void ImageEditorPrintDialogPage::toggleRatio( bool enable )
{
if (!enable) return;
// choosing a startup value of 15x10 cm (common photo dimention)
// mContent->mHeight->value() or mContent->mWidth->value()
// are usually empty at startup and hxw (0x0) is not good IMO keeping ratio
double hValue, wValue;
if (d->image.height() > d->image.width())
{
hValue = d->height->value();
if (!hValue) hValue = 150*unitToMM(d->previousUnit);
wValue = (d->image.width() * hValue)/ d->image.height();
}
else
{
wValue = d->width->value();
if (!wValue) wValue = 150*unitToMM(d->previousUnit);
hValue = (d->image.height() * wValue)/ d->image.width();
}
d->width->blockSignals(true);
d->height->blockSignals(true);
d->width->setValue(wValue);
d->height->setValue(hValue);
d->width->blockSignals(false);
d->height->blockSignals(false);
}
void ImageEditorPrintDialogPage::slotUnitChanged(const TQString& string)
{
Unit newUnit = stringToUnit(string);
double ratio = unitToMM(d->previousUnit) / unitToMM(newUnit);
d->width->blockSignals(true);
d->height->blockSignals(true);
d->width->setValue( d->width->value() * ratio);
d->height->setValue( d->height->value() * ratio);
d->width->blockSignals(false);
d->height->blockSignals(false);
d->previousUnit = newUnit;
}
void ImageEditorPrintDialogPage::readSettings()
{
TDEConfig* config = kapp->config();
config->setGroup("Color Management");
d->cmEnabled = config->readBoolEntry("EnableCM", false);
}
void ImageEditorPrintDialogPage::slotSetupDlg()
{
EditorWindow* editor = dynamic_cast<EditorWindow*>(d->parent);
editor->setup(true);
}
void ImageEditorPrintDialogPage::slotAlertSettings( bool t)
{
if (t && !d->cmEnabled)
{
TQString message = i18n("<p>Color Management is disabled.</p> \
<p>You can enable it now by clicking on the \"Settings\" button.</p>");
KMessageBox::information(this, message);
d->colorManaged->setChecked(!t);
}
}
double ImageEditorPrintDialogPage::unitToMM(Unit unit)
{
if (unit == DK_MILLIMETERS)
{
return 1.;
}
else if (unit == DK_CENTIMETERS)
{
return 10.;
}
else
{ //DK_INCHES
return 25.4;
}
}
ImageEditorPrintDialogPage::Unit ImageEditorPrintDialogPage::stringToUnit(const TQString& unit)
{
if (unit == i18n("Millimeters"))
{
return DK_MILLIMETERS;
}
else if (unit == i18n("Centimeters"))
{
return DK_CENTIMETERS;
}
else
{//Inches
return DK_INCHES;
}
}
TQString ImageEditorPrintDialogPage::unitToString(Unit unit)
{
if (unit == DK_MILLIMETERS)
{
return i18n("Millimeters");
}
else if (unit == DK_CENTIMETERS)
{
return i18n("Centimeters");
}
else
{ //DK_INCHES
return i18n("Inches");
}
}
} // namespace Digikam