summaryrefslogblamecommitdiffstats
path: root/src/maingui/printergui.cpp
blob: 8bc9133f31e96ec9e07837e18fc3f309847b0e64 (plain) (tree)
1
2
3
4
5
6
7
8
9

                          
                      

                   
                     
                      
                    
                
 

                                                                                   
                                                                  
                                                                      
 



                                             

                                                                               
                        
                               
                   


                        
 
                                                                           
 

                                                              
                                               


















                                                                                                                                                          

                                                 

                                 


                                                     
 


                                                                                            
 

                                         
 

                        
 



                                                                           
                                           

 
                                                                               

                         




                                                                                  



                                        

 
                                                                               

                               
                          


                                                                    
                                                                  



                                             
 



                                                                             


                                           
                                                                                    







                                            

                                        

                                                     
                                                     
                                             

















                                                                                                        

                                                                      


                                                                                                          



                                                                    
                                       
                 
         







                                                           



                                                                          








                                                                                                                 
 



                               

 







                                                                  





                                                                                                

                                                        




                                                                             






















                                                                          
                                                                                                                              






                                                                                               

                                                                                           


                                                 
                                                        

                                                                  

                                                                                                                                 


                                                                  
                                                       
                                            


                                      


                                                   



                                                                             
 
 
                                                                               

                                                                                                       
                                    
 




                                                                                                   

 
                                                                        
 





                                                                                           
 












                                                                                                                    
                          
                                           
                                                                                                 




                                                                                                                      


                                                                                                           
                                        
                
                                                                                                              










                                                      
 


                                                   
                                                                                           

                                                  
                                          
         

 

























                                                                                                           
                                                                               

                                          
                      
 
 
                                                                               

                                         
                    
 
                                    
 





                                                                                           
 

                                              
                
                                                                         
                                  
                                                                                
                                                                                                                                                  

                                                                     
         
                 
                                                                          
                                   
                                                                                 








                                                                                                                                                  

                                                                     
         

                                              
                                                           




                                                                                                   

                                                                                               
                                                             

                                                                                 

                                        






                                                                                  
                                                                              


                                                                                       

         








                                                             
                           







                                      




                                                                                                     

                  
                                         


                                                 

                                              

                                                                                  
                                    
                                                                           





                                                      
         
 
 
 



                                                           

                                       






                                                                           
 












































































































































































































































































                                                                     
#include "printergui.h"
#include "ui_printergui.h"
#include <QMessageBox>
#include <unistd.h>
#include <QTimer>
#include <QStatusBar>
#include <QCloseEvent>
#include <QFileInfo>
#include <pwd.h>

static QStringList knownColorOptions = QStringList() << "ColorModel" << "XRXColor";
static QStringList knownGrayscaleOptions = QStringList() << "HPColorAsGray";
static QStringList knownDuplexOptions = QStringList() << "Duplex";
static QStringList knownPageSizeOptions = QStringList() << "PageSize";

static int isUtf8(const char *s);
static void toLatin1(char *s);
static char* cleanCupsString(const char* in);

// ____________________________________________________________________________
PrinterGui::PrinterGui(char *argv[], QWidget *parent) :
	QDialog(parent),
	ui(new Ui::PrinterGui),
	user(NULL),
	statusBar(NULL),
	bgTimeout(-1),
	jobId(0)
{
	// When called it is guaranteed that argv has (at least) 3 elements

	// Set username
	// Do not use getlogin[_r] as it doesn't fucking work!
	struct passwd *pw = getpwuid(getuid());
	if (pw != NULL && pw->pw_dir != NULL && pw->pw_dir[0] == '/') {
		// Special case - .account file in user home, but only if it is owned by root
		QFile file(QString(pw->pw_dir).append("/.account"));
		if (file.exists()) {
			QFileInfo fi(file);
			// If owned by root, not writable by group or other, read user stored inside
			if ((fi.permissions() & (QFile::WriteGroup | QFile::WriteOther)) == 0 && fi.owner() == "root" && file.open(QIODevice::ReadOnly)) {
				char buffer[20];
				qint64 ret = file.readLine(buffer, sizeof(buffer));
				if (ret > 0) {
					if (buffer[ret - 1] == '\n') {
						buffer[ret - 1] = '\0';
					}
					this->user = strdup(buffer);
				}
			}
		}
	}
	if (this->user == NULL && pw != NULL && pw->pw_name != NULL && pw->pw_name[0] != '\0') {
		// Detect real name
		this->user = strdup(pw->pw_name);
	}
	if (this->user == NULL) {
		// Fallback to what command line says
		this->user = strdup(argv[1]);
	}

	// Filename
	this->file = new char[strlen(argv[2]) + 10]; // + 10 in case we rename (ghostscript)
	strcpy(this->file, argv[2]);

	// Initialize cups
	num_dests = cupsGetDests(&dests);

	// Initialize UI
	initializeUI();

	// Timer
	this->bgTimer = new QTimer(this);
	connect(bgTimer, SIGNAL(timeout()), this, SLOT(bgTimer_timeout()));
	this->bgTimer->start(1000);
	ui->txtUserId->setText(this->user);
}

// ____________________________________________________________________________
PrinterGui::~PrinterGui()
{
	QMap<QTreeWidgetItem*, ppd_file_t*>::const_iterator i = ppds.constBegin();
	while (i != ppds.constEnd()) {
		ppdClose(i.value());
	}
	ppds.clear();
	cupsFreeDests(num_dests, dests);
	free(this->user);
	delete[] this->file;
	delete this->ui;
}

// ____________________________________________________________________________
void PrinterGui::initializeUI()
{
	ui->setupUi(this);
	this->setWindowModality(Qt::ApplicationModal);
	// Put always on top
	this->setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint);
	ui->horizontalLayoutButtons->setAlignment(Qt::AlignRight);
	ui->comboBoxColor->setEnabled(false);
	ui->comboBoxSides->setEnabled(false);
	ui->label_color->setEnabled(false);
	ui->label_duplex->setEnabled(false);

	// Create a status bar (qt designer doesn't support this for dialogs)
	statusBar = new QStatusBar(this);
	ui->statusBarLayout->addWidget(statusBar);

	/*  Initialize Treeview  */

	ui->printerList->setColumnCount(3);
	//ui->printerList->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum);

	// Rename headers
	QStringList h;
	h.append("Drucker");
	h.append("Information");
	h.append("Standort");
	ui->printerList->setHeaderLabels(h);

	// Hide trusted print by default
	handleTrustedPrint(NULL);
	// Fill treewidget with data from cups dests;
	cups_dest_t *dest = dests;
	for (int i = num_dests; i > 0; ++dest, --i) {
		if (dest->instance == NULL) {
			char uri[1000];
			ipp_t *req = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
			httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
					"localhost", ippPort(), "/printers/%s", dest->name);
			ippAddString(req, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
			NULL, uri);
			ippAddString(req, IPP_TAG_OPERATION, IPP_TAG_NAME,
					"requesting-user-name",
					NULL, cupsUser());
			ipp_t *res = cupsDoRequest(CUPS_HTTP_DEFAULT, req, "/");
			ipp_attribute_t *attr = ippFindAttribute(res, "printer-location", IPP_TAG_TEXT);
			const char *location = "";
			if (attr != NULL) {
				location = ippGetString(attr, 0, NULL);
				if (location == NULL) {
					location = "";
				}
			}
			QTreeWidgetItem *wi = new QTreeWidgetItem();
			wi->setText(0, QString::fromUtf8(dest->name));
			wi->setText(1, QString::fromUtf8(
					cupsGetOption("printer-info", dest->num_options, dest->options)));
			wi->setText(2, QString::fromUtf8(location));
			ui->printerList->addTopLevelItem(wi);
			if (dest->is_default) {
				ui->printerList->setCurrentItem(wi);
			}
			ippDelete(res);
		}
	}

	// Resize columns to contents
	for (int i = 0; i < 3; ++i) {
		ui->printerList->resizeColumnToContents(i);
	}

	/*  Main Window properties  */

	// center dialog on screen center
	QRect desktopRect = QApplication::desktop()->screenGeometry(this);
	this->move( desktopRect.width()/2-this->width()/2,
	            desktopRect.height()/2-this->height()/2);
	const char *docName;
	docName = getenv("J");
	if (docName == NULL) {
		docName = getenv("N");
	}
	if (docName == NULL) {
		docName = "Untitled";
	}
	this->setWindowTitle(QString::fromUtf8("Drucken - %1 [%2]").arg(this->user, QString::fromUtf8(docName)));

	this->show();
	this->showNormal();
	this->raise();
	this->activateWindow();
}

void PrinterGui::handleTrustedPrint(ppd_file_t *ppd)
{
	bool supported = false;
	bool enabled = false;
	QString pwProperty, pwMask;
	static QRegExpValidator sevenNum(QRegExp("^[0-9]{1,7}$"));
	QRegExpValidator *pwValidator = NULL;

	if (ppd != NULL) {
		for (ppd_option_t *o = ppdFirstOption(ppd); o != NULL; o = ppdNextOption(ppd)) {
			if (strcmp(o->keyword, "UseSecure") == 0) {
				supported = true;
				enabled = (strcmp(o->defchoice, "On") == 0);
			} else if (strcmp(o->keyword, "SecuredPassword") == 0) {
				pwProperty = o->keyword;
				pwValidator = &sevenNum;
			} else if (strcmp(o->keyword, "UserPassword") == 0) {
				if (pwProperty.isEmpty()) {
					pwProperty = o->keyword;
					pwValidator = &sevenNum;
				}
			}
		}
	}
	ui->txtUserPin->setProperty("key", pwProperty);
	ui->txtUserPin->setValidator(pwValidator);
	ui->chkSecurePrint->setChecked(enabled);
	for (int i = 0; i < ui->gridSecurePrint->count(); ++i) {
		QWidget *child = ui->gridSecurePrint->itemAt(i)->widget();
		if (child != NULL) {
			child->setVisible(supported);
		}
	}
}

void PrinterGui::on_chkSecurePrint_stateChanged(int state)
{
	ui->txtUserId->setEnabled(state == Qt::Checked);
	ui->txtUserPin->setEnabled(state == Qt::Checked);
	if (state == Qt::Checked) {
		ui->txtUserPin->setFocus();
	}
}

static bool enableOptionSelection(ppd_file_t *ppd, QStringList &nameList, QComboBox *combo, QLabel *label, bool forceDisabled)
{
	// Check for each option if it matches an
	QString matchedProperty;

	for (ppd_option_t *o = ppdFirstOption(ppd); o != NULL; o = ppdNextOption(ppd)) {
		QString option(o->keyword);
		for (QStringList::iterator it = nameList.begin(); it != nameList.end(); ++it) {
			bool exact = (option == *it);
			if((!exact && !matchedProperty.isEmpty()) || !option.contains(*it))
				continue;
			// Matches, update combo
			matchedProperty = option;
			combo->setUpdatesEnabled(false);
			combo->clear();
			for (int i = 0; i < o->num_choices; ++i) {
				combo->addItem(QString::fromUtf8(o->choices[i].text), QString::fromLatin1(o->choices[i].choice));
				if (strcmp(o->choices[i].choice, o->defchoice) == 0) {
					combo->setCurrentIndex(i);
				}
			}
			combo->setUpdatesEnabled(true);
			combo->adjustSize();
			if (exact) {
				break;
			}
		}
	}
	combo->setProperty("key", matchedProperty);
	const bool selectable = !matchedProperty.isEmpty() && !forceDisabled;
	combo->setEnabled(selectable);
	label->setEnabled(selectable);
	return selectable;
}

// ____________________________________________________________________________
void PrinterGui::on_printerList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
	ui->printerList->setFocus();

	cups_dest_t *dest = cupsGetDest(
		                      ui->printerList->currentItem()->text(0).toUtf8().constData(),
		                      NULL,
		                      num_dests,
		                      dests);


	/* * Check printer properties (auth, color, duplex, copies) * */

	// get printer capabilities
	const char *type = cupsGetOption("printer-type", dest->num_options, dest->options);
	int res = 0;
	if (type != NULL) {
		res = ::atoi(type);
	}

	ppd_file_t *ppd = NULL;
	if (ppds.contains(current)) {
		ppd = ppds[current];
	} else {
		const char *ppdFile = cupsGetPPD2(CUPS_HTTP_DEFAULT, dest->name);
		if (ppdFile == NULL) {
			qDebug() << "cupsGetPPD2 for " << dest->name << " returned NULL: " << cupsLastErrorString();
		} else {
			ppd = ppdOpenFile(ppdFile);
			qDebug() << dest->name << " has ppd " << ppdFile;
		}
		ppds.insert(current, ppd);
	}
	if (ppd != NULL) {
		// Check color capabilities
		//if (res & CUPS_PRINTER_COLOR) // No needed? Should just get disabled either way
		if (enableOptionSelection(ppd, knownGrayscaleOptions, ui->comboBoxColor, ui->label_color, false)) {
			ui->label_color->setText(trUtf8("Graustufen"));
		} else if (enableOptionSelection(ppd, knownColorOptions, ui->comboBoxColor, ui->label_color, false)) {
			ui->label_color->setText(trUtf8("Farbe"));
		}
		enableOptionSelection(ppd, knownDuplexOptions, ui->comboBoxSides, ui->label_duplex, false);
		// TODO: Make it so this selection overrides what the document specifies
		enableOptionSelection(ppd, knownPageSizeOptions, ui->cboPaperSize, ui->lblPaperSize, true);
		handleTrustedPrint(ppd);
	} else {
		qDebug() << "ppd is null for "<< dest->name << "; cups last error: " << cupsLastErrorString();
	}

	// Check copy capabilities
	if (res & CUPS_PRINTER_COPIES) {
		ui->lineEditCopies->setEnabled(true);
		ui->labelCopies->setEnabled(true);
	} else {
		ui->lineEditCopies->setEnabled(false);
		ui->lineEditCopies->setText("1");
		ui->labelCopies->setEnabled(false);
	}

	// Check availability
	if (res & CUPS_PRINTER_REJECTING) {
		ui->buttonPrint->setEnabled(false);
		statusBar->showMessage("Dieser Drucker nimmt zur Zeit keine Aufträge an");
	} else {
		ui->buttonPrint->setEnabled(true);
		statusBar->clearMessage();
	}
}

void PrinterGui::closeEvent(QCloseEvent * e)
{
	if (e->isAccepted()) {
		if (jobId != 0) {
			cupsCancelJob(ui->printerList->currentItem()->text(0).toUtf8().constData(), jobId);
			jobId = 0;
		}
		QCoreApplication::instance()->exit(0);
	}
	QDialog::closeEvent(e);
}

void PrinterGui::keyPressEvent(QKeyEvent * e)
{
	if(e->key() != Qt::Key_Escape) {
		QDialog::keyPressEvent(e);
		return;
	}
	this->close();
}

void PrinterGui::hideEvent(QHideEvent * e)
{
	this->close();
}

// ____________________________________________________________________________
void PrinterGui::on_buttonCancel_clicked()
{
	this->close();
}

// ____________________________________________________________________________
void PrinterGui::on_buttonPrint_clicked()
{
	QString cmd;

	/* * Print via cups lib * */

	// Destination / Queue
	cups_dest_t *dest = cupsGetDest(
	                      ui->printerList->currentItem()->text(0).toUtf8().constData(),
	                      NULL,
	                      num_dests,
	                      dests);

	// In dest we have the default options

	// Color
	QString colorKey = ui->comboBoxColor->property("key").toString();
	if (!colorKey.isEmpty()) {
		dest->num_options = cupsAddOption(colorKey.toUtf8().constData(),
		                                   ui->comboBoxColor->itemData(ui->comboBoxColor->currentIndex()).toString().toUtf8().constData(),
		                                   dest->num_options,
		                                   &(dest->options));
	}
	// Duplex
	QString duplexKey = ui->comboBoxSides->property("key").toString();
	if (!duplexKey.isEmpty()) {
		dest->num_options = cupsAddOption(duplexKey.toUtf8().constData(),
		                                   ui->comboBoxSides->itemData(ui->comboBoxSides->currentIndex()).toString().toUtf8().constData(),
		                                   dest->num_options,
		                                   &(dest->options));
	}
	// Paper size
	QString paperKey = ui->cboPaperSize->property("key").toString();
	if (!paperKey.isEmpty()) {
		dest->num_options = cupsAddOption(paperKey.toUtf8().constData(),
		                                   ui->cboPaperSize->itemData(ui->cboPaperSize->currentIndex()).toString().toUtf8().constData(),
		                                   dest->num_options,
		                                   &(dest->options));
	}
	// Kopien
	if (ui->lineEditCopies->isEnabled()) {
		dest->num_options = cupsAddOption("copies",
		                                   ui->lineEditCopies->text().toUtf8().constData(),
		                                   dest->num_options,
		                                   &(dest->options));
	}

	dest->num_options = cupsRemoveOption("InputSlot", dest->num_options, &(dest->options));

	// If we use secured print, allow overriding username
	const char *securedMode = "Off";
	if (ui->chkSecurePrint->isHidden() || !ui->chkSecurePrint->isChecked()) {
		cupsSetUser(this->user);
	} else {
		securedMode = "On";
		if (ui->txtUserId->text().isEmpty()) {
			cupsSetUser(this->user);
		} else {
			cupsSetUser(ui->txtUserId->text().toUtf8().constData());
		}
		const QString pinKey = ui->txtUserPin->property("key").toString();
		dest->num_options = cupsAddOption(pinKey.toUtf8().constData(),
					   ui->txtUserPin->text().toUtf8().constData(),
					   dest->num_options,
					   &(dest->options));
	}

	dest->num_options = cupsAddOption("UseSecured",
					   securedMode,
					   dest->num_options,
					   &(dest->options));
	dest->num_options = cupsAddOption("OptSecured",
					   securedMode,
					   dest->num_options,
					   &(dest->options));

	char jobtitle[250];
	const char *docName;
	docName = getenv("J");
	if (docName == NULL) {
		docName = getenv("N");
	}
	if (docName == NULL) {
		docName = "Untitled";
	}
	char *cleanUser = cleanCupsString(this->user);
	char *cleanTitle = cleanCupsString(docName);
	snprintf(jobtitle, sizeof(jobtitle), "gui-%d-%s (%s)", (int)getpid(), cleanUser, cleanTitle);
	free(cleanUser);
	free(cleanTitle);

	// Drucken
	jobId = cupsPrintFile(dest->name,
	                       file,
	                       jobtitle,
	                       dest->num_options,
	                       dest->options);
	if (jobId == 0) {
		QMessageBox::critical(this, "CUPS Fehler", cupsLastErrorString());
	} else {
		this->bgTimeout = 0;
		statusBar->showMessage("Druckauftrag wird verarbeitet...");
		ui->buttonPrint->setEnabled(false);
		ui->cboPaperSize->setEnabled(false);
		ui->comboBoxColor->setEnabled(false);
		ui->comboBoxSides->setEnabled(false);
		ui->lineEditCopies->setEnabled(false);
		ui->printerList->setEnabled(false);
	}

}

void PrinterGui::bgTimer_timeout()
{
	if (this->bgTimeout == -1) {
		// Could do something here every second....
		this->raise();
		this->activateWindow();
		return;
	}
	if (++this->bgTimeout > 120) {
		// Job was sent, GUI is invisible, quit after a few seconds
		QCoreApplication::instance()->exit(0);
	}
}

/**
 * Checks if the given c string is UTF-8.
 * Using as bool-function:
 * Return value != 0 means (potentially) UTF-8,
 * that is, it can be processed by UTF-8 functions without error.
 * Using for in-detail detection:
 * Return value 0 = not valid UTF-8
 * Return value 1 = valid ASCII (which is a subset of UTF-8)
 * Return value 2 = valid UTF-8, but not valid ASCII.
 */
static int isUtf8(const char *s)
{
	bool isAscii = true;
	while (*s != '\0') {
		if ((s[0] & 248) == 240 &&
				(s[1] & 192) == 128 &&
				(s[2] & 192) == 128 &&
				(s[3] & 192) == 128) {
			s += 4;
			isAscii = false;
		} else if ((s[0] & 240) == 224 &&
				(s[1] & 192) == 128 &&
				(s[2] & 192) == 128) {
			s += 3;
			isAscii = false;
		} else if ((s[0] & 224) == 192 &&
				(s[1] & 192) == 128) {
			s += 2;
			isAscii = false;
		} else if ((s[0] & 128) != 0) {
			// Invalid UTF-8 sequence, so no ASCII either
			return 0;
		} else {
			s+= 1;
		}
	}
	return isAscii ? 1 : 2;
}

static void toLatin1(char *s)
{
	char *d = s;
	while (*s != '\0') {
		if ((s[0] & 248) == 240 &&
				(s[1] & 192) == 128 &&
				(s[2] & 192) == 128 &&
				(s[3] & 192) == 128) {
			d[0] = '_';
			s += 4;
		} else if ((s[0] & 240) == 224 &&
				(s[1] & 192) == 128 &&
				(s[2] & 192) == 128) {
			d[0] = '_';
			s += 3;
		} else if ((s[0] & 224) == 192 &&
				(s[1] & 192) == 128) {
			int c = ((s[0] & 31) << 6) | (s[1] & 63);
			if (c > 255) {
				d[0] = '_';
			} else {
				d[0] = (char)c;
			}
			s += 2;
		} else if ((s[0] & 128) != 0) {
			// Invalid UTF-8 sequence!?
			d[0] = '_';
			s+= 1;
		} else {
			d[0] = s[0];
			s+= 1;
		}
		d += 1;
	}
	*d = '\0';
}

static char* cleanCupsString(const char* in)
{
	int enc = isUtf8(in);
	char *out = strdup(in);
	if (enc == 2) {
		// UTF-8, turn to Latin-1
		toLatin1(out);
	}
	// Try to convert umlauts etc., remove non-printing chars
	unsigned char *tmp = (unsigned char*)out;
	while (*tmp != '\0') {
		if (*tmp < 0x20 || *tmp > 0x7E) {
			switch (*tmp) {
			case 0x84: // „
			case 0x93: // “
			case 0x94: // ”
			case 0xAB: // «
			case 0xBB: // »
				*tmp = '"';
				break;
			case 0x91: // ‘
			case 0x92: // ’
			case 0xB4: // ´
				*tmp = '\'';
				break;
			case 0xB7: // ·
				*tmp = '*';
				break;
			case 0xF7: // ÷
				*tmp = '/';
				break;
			case 0x82: // ‚
			case 0xB8: // ¸
				*tmp = ',';
				break;
			case 0x96: // –
			case 0x97: // —
			case 0xAC: // ¬
				*tmp = '-';
				break;
			case 0xAF: // ¯
			case 0xB0: // °
				*tmp = '^';
				break;
			case 0x98: // ˜
				*tmp = '~';
				break;
			case 0xB9: // ¹
				*tmp = '1';
				break;
			case 0xB2: // ²
				*tmp = '2';
				break;
			case 0xB3: // ³
				*tmp = '3';
				break;
			case 0xC0: // À
			case 0xC1: // Á
			case 0xC2: // Â
			case 0xC3: // Ã
			case 0xC4: // Ä
			case 0xC5: // Å
			case 0xC6: // Æ
				*tmp = 'A';
				break;
			case 0xAA: // ª
			case 0xE0: // à
			case 0xE1: // á
			case 0xE2: // â
			case 0xE3: // ã
			case 0xE4: // ä
			case 0xE5: // å
			case 0xE6: // æ
				*tmp = 'a';
				break;
			case 0xA9: // ©
			case 0xC7: // Ç
				*tmp = 'C';
				break;
			case 0xA2: // ¢
			case 0xE7: // ç
				*tmp = 'c';
				break;
			case 0xD0: // Ð
				*tmp = 'D';
				break;
			case 0xF0: // ð
				*tmp = 'd';
				break;
			case 0xC8: // È
			case 0xC9: // É
			case 0xCA: // Ê
			case 0xCB: // Ë
				*tmp = 'E';
				break;
			case 0xE8: // è
			case 0xE9: // é
			case 0xEA: // ê
			case 0xEB: // ë
				*tmp = 'e';
				break;
			case 0x83: // ƒ
				*tmp = 'f';
				break;
			case 0xCC: // Ì
			case 0xCD: // Í
			case 0xCE: // Î
			case 0xCF: // Ï
				*tmp = 'I';
				break;
			case 0xA1: // ¡
			case 0xEC: // ì
			case 0xED: // í
			case 0xEE: // î
			case 0xEF: // ï
				*tmp = 'i';
				break;
			case 0xD1: // Ñ
				*tmp = 'N';
				break;
			case 0xF1: // ñ
				*tmp = 'n';
				break;
			case 0xD2: // Ò
			case 0xD3: // Ó
			case 0xD4: // Ô
			case 0xD5: // Õ
			case 0xD6: // Ö
			case 0xD8: // Ø
				*tmp = 'O';
				break;
			case 0xF2: // ò
			case 0xF3: // ó
			case 0xF4: // ô
			case 0xF5: // õ
			case 0xF6: // ö
			case 0xF8: // ø
				*tmp ='o';
				break;
			case 0xA3: // £
			case 0xA7: // §
				*tmp = 'P';
				break;
			case 0xAE: // ®
				*tmp = 'R';
				break;
			case 0x8A: // Š
				*tmp = 'S';
				break;
			case 0x9A: // š
			case 0xDF: // ß
				*tmp = 's';
				break;
			case 0xDE: // Þ
				*tmp = 'T';
				break;
			case 0xFE: // þ
				*tmp = 't';
				break;
			case 0xD9: // Ù
			case 0xDA: // Ú
			case 0xDB: // Û
			case 0xDC: // Ü
				*tmp = 'U';
				break;
			case 0xB5: // µ
			case 0xF9: // ù
			case 0xFA: // ú
			case 0xFB: // û
			case 0xFC: // ü
				*tmp = 'u';
				break;
			case 0xD7: // ×
				*tmp = 'x';
				break;
			case 0xA5: // ¥
			case 0xDD: // Ý
				*tmp = 'Y';
				break;
			case 0xFD: // ý
			case 0xFF: // ÿ
				*tmp = 'y';
				break;
			default:
				*tmp = '.';
				break;
			}
		}
		tmp++;
	}
	return out;
}