Actually add old project files

This commit is contained in:
Anton Thomasson 2019-12-01 20:27:00 +01:00
parent c5ef6a6982
commit 210e40920c
18 changed files with 2047 additions and 0 deletions

View file

@ -0,0 +1,70 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Setting {
property var choices
function prettifyChoice(name, value)
{
switch(name) {
case "print-quality":
switch(value) {
case 3:
return "draft";
case 4:
return "normal";
case 5:
return "high";
default:
return "unknown quality "+value
}
case "orientation-requested":
switch(value) {
case 3:
return "portrait";
case 4:
return "landscape";
case 5:
return "reverse landscape";
case 6:
return "reverse portrait";
default:
return "unknown orientation "+value
}
case "printer-resolution":
var units = "";
if(value.units==3) {
units="dpi";
} else if (units==4){
units="dots/cm"
}
return ""+value.x+"x"+value.y+units;
}
return value;
}
ValueButton {
enabled: valid
anchors.verticalCenter: parent.verticalCenter
label: prettyName
value: prettifyChoice(name, choice ? choice : default_choice)
onClicked: parent.clicked()
}
property var menu: ContextMenu {
id: menu
Repeater {
model: choices
MenuItem {
text: prettifyChoice(name, choices[index])
onClicked:
{
choice = choices[index];
}
}
}
}
}

52
qml/pages/InputDialog.qml Normal file
View file

@ -0,0 +1,52 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import seaprint.ippprinter 1.0
Dialog {
id: dialog
property string value
property string title
canAccept: label.text != label._default
Column {
width: parent.width
DialogHeader { }
TextField {
id: valueField
width: parent.width
placeholderText: "192.168.1.1/ipp/print"
label: title
}
Label {
id: label
x: Theme.paddingLarge
property string _default: "No printer found"
text: _default
}
IppPrinter {
id: printer
url: valueField.text
onAttrsChanged: {
if(printer.attrs["printer-name"]) {
label.text = "Found: "+printer.attrs["printer-name"].value
}
else
{
label.text = label._default
}
}
}
}
onDone: {
if (result == DialogResult.Accepted) {
value = valueField.text
}
}
}

View file

@ -0,0 +1,43 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Dialog {
id: dialog
property int value
property int min;
property int max;
property string title
canAccept: valueField.acceptableInput
Component.onCompleted:
{
console.log("adasdsa", title)
}
Column {
width: parent.width
DialogHeader { }
TextField {
id: valueField
validator: IntValidator{bottom: min; top: max;}
width: parent.width
placeholderText: ""+min+"-"+max
label: title
focus: true
labelVisible: true
inputMethodHints: Qt.ImhDigitsOnly
}
}
onDone: {
if (result == DialogResult.Accepted) {
value = valueField.text
}
}
}

View file

@ -0,0 +1,50 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Setting {
property int low
property int high
ValueButton {
enabled: valid
anchors.verticalCenter: parent.verticalCenter
label: prettyName
value: choice ? choice : default_choice
onClicked: parent.clicked()
}
property var menu: ContextMenu {
id: menu
MenuItem {
Slider
{
minimumValue: low
maximumValue: high < 100 ? high : 100
width: parent.width
stepSize: 1
value: choice
onValueChanged:
{
choice = value;
}
}
IconButton
{
anchors.right: parent.right
icon.source: "image://theme/icon-s-edit"
onClicked: {var dialog = pageStack.push(Qt.resolvedUrl("IntegerInputDialog.qml"),
{value: choice, title: prettyName,
min: low, max: high});
dialog.accepted.connect(function() {
choice = dialog.value;
})
}
}
}
}
}

130
qml/pages/PrinterPage.qml Normal file
View file

@ -0,0 +1,130 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import "utils.js" as Utils
Page {
id: page
property var printer
property var jobParams: new Object();
property string selectedFile
Component.onCompleted: {
console.log(JSON.stringify(printer.attrs))
}
Connections {
target: printer
onJobAttrsChanged: {
notifier.notify(printer.jobAttrs["job-state-message"].value)
}
}
// To enable PullDownMenu, place our content in a SilicaFlickable
SilicaFlickable {
anchors.fill: parent
// PullDownMenu and PushUpMenu must be declared in SilicaFlickable, SilicaListView or SilicaGridView
PullDownMenu {
MenuItem {
text: qsTr("Print")
onClicked: {
console.log(JSON.stringify(jobParams))
printer.print(jobParams, page.selectedFile)
}
}
}
ListModel {
id:mod
ListElement {name: "sides"; prettyName: "Sides"; tag: 0x23}
ListElement {name: "copies"; prettyName: "Copies"; tag: 0x21}
// ListElement {name: "page-ranges"; prettyName: "Page range"; tag: 0x33}
ListElement {name: "print-color-mode"; prettyName: "Color mode"; tag: 0x23}
// ListElement {name: "orientation-requested"; prettyName: "Orientation"; tag: 0x23}
ListElement {name: "print-quality"; prettyName: "Quality"; tag: 0x23}
// Bleh, can't create the json object with another object as value, for whatever reason
ListElement {name: "printer-resolution"; prettyName: "Resolution"; tag: 0x32}
}
SilicaListView {
id: listView
model: mod
clip: true
anchors.fill: parent
width: parent.width
header: PageHeader {
id: pageHeader
title: printer.attrs["printer-name"].value
description: selectedFile
}
delegate: ListItem {
id: delegate
property alias loaderItem: loader.item
openMenuOnPressAndHold: false
Loader {
id: loader
anchors.fill: parent
property var menu
}
Component.onCompleted: {
console.log("handling", tag, name, prettyName, JSON.stringify(printer.attrs[name+"-supported"]), JSON.stringify(printer.attrs[name+"-default"]))
switch(tag) {
case 0x21:
loader.setSource("IntegerSetting.qml",
{name: name,
prettyName: prettyName,
tag: tag,
low: printer.attrs[name+"-supported"].value.low,
high: printer.attrs[name+"-supported"].value.high,
default_choice: printer.attrs[name+"-default"].value
})
break
case 0x33:
loader.setSource("RangeSetting.qml",
{name: name,
prettyName: prettyName,
valid: false, //TODO
tag: 0x33 // integer-range
})
break
case 0x32:
case 0x23:
loader.setSource("ChoiceSetting.qml",
{name: name,
prettyName: prettyName,
tag: tag,
choices: printer.attrs[name+"-supported"].value,
default_choice: printer.attrs[name+"-default"].value
})
break
}
}
onLoaderItemChanged: {
menu = loaderItem.menu
loaderItem.clicked.connect(function() {
openMenu()
})
loaderItem.choiceMade.connect(function(tag, choice) {
console.log("choice changed", tag, JSON.stringify(choice))
jobParams[name] = {tag: tag, value: choice};
console.log(JSON.stringify(jobParams));
})
}
}
VerticalScrollDecorator {}
}
}
}

View file

@ -0,0 +1,30 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Setting {
//TODO
property int low
property int high
property int choice_low
property int choice_high
ValueButton {
enabled: valid
anchors.verticalCenter: parent.verticalCenter
label: prettyName
value: choice ? choice : default_choice
onClicked: parent.clicked()
}
property var menu: ContextMenu {
id: menu
MenuItem {
}
}
}

21
qml/pages/Setting.qml Normal file
View file

@ -0,0 +1,21 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
anchors.verticalCenter: parent.verticalCenter
property string name
property string prettyName
property int tag
property bool valid: true
property var choice
property var default_choice
signal clicked()
signal choiceMade(int tag, var choice)
onChoiceChanged: choiceMade(tag, choice)
property var menu
}

56
qml/pages/printer.svg Normal file
View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="487.381px" height="487.381px" viewBox="0 0 487.381 487.381" style="enable-background:new 0 0 487.381 487.381;"
xml:space="preserve">
<g>
<g>
<path d="M400.1,100.918h-9.871V11.646C390.229,5.215,385.014,0,378.582,0H108.804c-6.436,0-11.646,5.215-11.646,11.646v89.271
h-9.877c-38.523,0-69.871,31.344-69.871,69.871v144.258c0,38.528,31.348,69.87,69.871,69.87h9.877v90.818
c0,6.432,5.21,11.646,11.646,11.646h269.778c6.432,0,11.646-5.215,11.646-11.646v-90.818h9.871
c38.523,0,69.871-31.342,69.871-69.87V170.789C469.971,132.261,438.623,100.918,400.1,100.918z M120.449,23.291h246.489v77.627
H120.449V23.291z M366.938,464.092H120.449V320.465h246.489V464.092z M446.682,315.046c0,25.687-20.896,46.581-46.582,46.581
h-9.871v-41.162h25.186c6.436,0,11.645-5.214,11.645-11.646c0-6.43-5.209-11.645-11.645-11.645h-36.832H108.804H75.421
c-6.431,0-11.646,5.215-11.646,11.645c0,6.433,5.215,11.646,11.646,11.646h21.737v41.162h-9.877
c-25.685,0-46.581-20.896-46.581-46.581V170.789c0-25.685,20.896-46.58,46.581-46.58h21.522H378.58h21.52
c25.684,0,46.58,20.896,46.58,46.58v144.257H446.682z"/>
<path d="M378.582,170.646c-8.771,0-15.893,7.119-15.893,15.893s7.119,15.893,15.893,15.893c8.775,0,15.895-7.118,15.895-15.893
C394.475,177.765,387.357,170.646,378.582,170.646z"/>
<path d="M147.618,360.734h192.146c6.432,0,11.646-5.213,11.646-11.646c0-6.43-5.215-11.645-11.646-11.645H147.618
c-6.431,0-11.646,5.215-11.646,11.645C135.972,355.521,141.187,360.734,147.618,360.734z"/>
<path d="M147.618,403.926h192.146c6.432,0,11.646-5.217,11.646-11.646c0-6.432-5.215-11.645-11.646-11.645H147.618
c-6.431,0-11.646,5.213-11.646,11.645S141.187,403.926,147.618,403.926z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

45
qml/pages/utils.js Normal file
View file

@ -0,0 +1,45 @@
function supported_formats(printer)
{
return _formats(printer).supported;
}
function format_filters(printer)
{
return _formats(printer).filters;
}
function _formats(printer)
{
var formats = printer.attrs["document-format-supported"].value;
var supported = [];
var filters = [];
if(has(formats, "application/pdf"))
{
supported.push("PDF");
filters.push("*.pdf");
}
if(has(formats, "image/jpeg"))
{
supported.push("JPEG");
filters.push("*.jpg");
filters.push("*.jpeg");
}
return {supported: supported.join(" "), filters: filters};
}
function has(arrayish, what)
{
for(var i in arrayish)
{
if(arrayish[i] == what)
return true
}
return false
}
function can_print(printer, fileName)
{
// Move to ippprinter?
return format_filters(printer).indexOf("*."+fileName.split('.').pop()) != -1;
}

67
rpm/harbour-seaprint.spec Normal file
View file

@ -0,0 +1,67 @@
#
# Do NOT Edit the Auto-generated Part!
# Generated by: spectacle version 0.27
#
Name: harbour-seaprint
# >> macros
# << macros
Summary: SeaPrint
Version: 0.1
Release: 1
Group: Qt/Qt
License: LICENSE
URL: http://example.org/
Source0: %{name}-%{version}.tar.bz2
Source100: harbour-seaprint.yaml
Requires: sailfishsilica-qt5 >= 0.10.9
BuildRequires: pkgconfig(sailfishapp) >= 1.0.2
BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5Quick)
BuildRequires: desktop-file-utils
%description
Short description of my Sailfish OS Application
%prep
%setup -q -n %{name}-%{version}
# >> setup
# << setup
%build
# >> build pre
# << build pre
%qmake5
make %{?_smp_mflags}
# >> build post
# << build post
%install
rm -rf %{buildroot}
# >> install pre
# << install pre
%qmake5_install
# >> install post
# << install post
desktop-file-install --delete-original \
--dir %{buildroot}%{_datadir}/applications \
%{buildroot}%{_datadir}/applications/*.desktop
%files
%defattr(-,root,root,-)
%{_bindir}
%{_datadir}/%{name}
%{_datadir}/applications/%{name}.desktop
%{_datadir}/icons/hicolor/*/apps/%{name}.png
# >> files
# << files

553
src/bytestream.cpp Normal file
View file

@ -0,0 +1,553 @@
#include "bytestream.h"
#include <iostream>
#include <cstdint>
#include <cstring>
using namespace std;
Bytestream::Bytestream()
{
_size = 0;
_pos = 0;
_noOfNextBytes = 0;
_noOfNextBytesValid = false;
_endianness = BigEndian;
}
Bytestream::Bytestream(size_t len)
{
_size = len;
_data = new uint8_t[_size];
memset(_data, 0, _size);
_pos = 0;
_noOfNextBytes = 0;
_noOfNextBytesValid = false;
_endianness = BigEndian;
}
Bytestream::Bytestream(const void* data, size_t len)
{
_size = len;
_data = new uint8_t[_size];
memcpy(_data, data, _size);
_pos = 0;
_noOfNextBytes = 0;
_noOfNextBytesValid = false;
_endianness = BigEndian;
}
Bytestream::Bytestream(const void* data, size_t len, Endianness e)
{
_size = len;
_data = new uint8_t[_size];
memcpy(_data, data, _size);
_pos = 0;
_noOfNextBytes = 0;
_noOfNextBytesValid = false;
_endianness = e;
}
Bytestream::Bytestream(const Bytestream& rhs)
{
_size = rhs._size;
_data = new uint8_t[_size];
memcpy(_data, rhs._data, _size);
_pos = rhs._pos;
_noOfNextBytes = 0;
_noOfNextBytesValid = false;
_endianness = rhs._endianness;
}
Bytestream::~Bytestream()
{
if(_size != 0)
{
delete _data;
}
}
bool Bytestream::operator==(const Bytestream& other) const
{
if(_size != other.size())
{
return false;
}
return memcmp(_data, other.raw(), _size) == 0;
}
bool Bytestream::operator!=(const Bytestream& other) const
{
if(_size != other.size())
{
return true;
}
return memcmp(_data, other.raw(), _size) != 0;
}
Bytestream& Bytestream::operator=(const Bytestream& other)
{
if(_size != 0)
{
delete _data;
}
_pos = other.pos();
_size = other.size();
_data = new uint8_t[_size];
memcpy(_data, other.raw(), _size);
return *this;
}
template <typename T>
T bswap(T u)
{
uint8_t* const p = reinterpret_cast<uint8_t*>(&u);
for (size_t i = 0; i < sizeof(T) / 2; i++)
{
std::swap(p[i], p[sizeof(T) - i - 1]);
}
return u;
}
#define GET(type, shorthand, len) GET_(type##len##_t, shorthand##len, len)
#define GET_(type, shortType, len) \
type Bytestream::get##shortType() \
{type tmp; getBytes(&tmp, sizeof(type));\
if(needsSwap()){tmp=bswap(tmp);} return tmp;}
GET(uint, U, 8)
GET(uint, U, 16)
GET(uint, U, 32)
GET(uint, U, 64)
GET(int, S, 8)
GET(int, S, 16)
GET(int, S, 32)
GET(int, S, 64)
GET(float, F, 32)
GET(float, F, 64)
std::string Bytestream::getString()
{
if(!_noOfNextBytesValid)
{
throw invalid_argument("No length given");
}
char* cs = new char[_noOfNextBytes+1];
cs[_noOfNextBytes] = 0;
getBytes(cs, _noOfNextBytes);
string s = std::string(cs, _noOfNextBytes);
delete cs;
return s;
}
Bytestream Bytestream::getBytestream()
{
if(!_noOfNextBytesValid)
{
throw invalid_argument("No length given");
}
uint8_t* cs = new uint8_t[_noOfNextBytes];
getBytes(cs, _noOfNextBytes);
Bytestream other = Bytestream(cs, _noOfNextBytes);
delete cs;
return other;
}
std::string Bytestream::getString(size_t len)
{
if(_noOfNextBytesValid && len != _noOfNextBytes)
{
throw logic_error("Desired lengths does not match");
}
else if(!_noOfNextBytesValid)
{
setNoOfNextBytes(len);
}
return getString();
}
Bytestream Bytestream::getBytestream(size_t len)
{
if(!_noOfNextBytesValid && len != _noOfNextBytes)
{
throw logic_error("Desired lengths does not match");
}
setNoOfNextBytes(len);
return getBytestream();
}
void Bytestream::getBytes(void* cs, size_t len)
{
_before(len);
memcpy(cs, &(_data[_pos]), len);
_after(len);
}
#define PEEK(type, shorthand, len) PEEK_(type##len##_t, shorthand##len, len)
#define PEEK_(type, shortType, len) \
type Bytestream::peek##shortType() \
{type tmp; getBytes(&tmp, sizeof(type));\
if(needsSwap()){tmp=bswap(tmp);} (*this) -= sizeof(type); return tmp;}
PEEK(uint, U, 8)
PEEK(uint, U, 16)
PEEK(uint, U, 32)
PEEK(uint, U, 64)
PEEK(int, S, 8)
PEEK(int, S, 16)
PEEK(int, S, 32)
PEEK(int, S, 64)
PEEK(float, F, 32)
PEEK(float, F, 64)
std::string Bytestream::peekString()
{
if(!_noOfNextBytesValid)
{
throw invalid_argument("No length given");
}
char* cs = new char[_noOfNextBytes+1];
cs[_noOfNextBytes] = 0;
getBytes(cs, _noOfNextBytes);
string s = std::string(cs, _noOfNextBytes);
delete cs;
(*this) -= _noOfNextBytes;
return s;
}
Bytestream Bytestream::peekBytestream()
{
if(!_noOfNextBytesValid)
{
throw invalid_argument("No length given");
}
uint8_t* cs = new uint8_t[_noOfNextBytes];
getBytes(cs, _noOfNextBytes);
Bytestream other = Bytestream(cs, _noOfNextBytes);
delete cs;
(*this) -= _noOfNextBytes;
return other;
}
std::string Bytestream::peekString(size_t len)
{
if(_noOfNextBytesValid && len != _noOfNextBytes)
{
throw logic_error("Desired lengths does not match");
}
else if(!_noOfNextBytesValid)
{
setNoOfNextBytes(len);
}
return peekString();
}
Bytestream Bytestream::peekBytestream(size_t len)
{
if(!_noOfNextBytesValid && len != _noOfNextBytes)
{
throw logic_error("Desired lengths does not match");
}
setNoOfNextBytes(len);
return peekBytestream();
}
#define NEXT(type, shorthand, len) NEXT_(type##len##_t, shorthand##len)
#define NEXT_(type, shortType) \
bool Bytestream::next##shortType(type u) \
{if(u == get##shortType())\
{return true;} \
else\
{(*this) -= sizeof(type);\
return false;}}
NEXT(uint, U, 8)
NEXT(uint, U, 16)
NEXT(uint, U, 32)
NEXT(uint, U, 64)
NEXT(int, S, 8)
NEXT(int, S, 16)
NEXT(int, S, 32)
NEXT(int, S, 64)
NEXT(float, F, 32)
NEXT(float, F, 64)
bool Bytestream::nextString(const std::string& s)
{
if(_noOfNextBytesValid && getNoOfNextBytes() != s.length())
{
throw logic_error("Desired length does not match const length");
}
else if(!_noOfNextBytesValid)
{
setNoOfNextBytes(s.length());
}
size_t noOfNextBytes = getNoOfNextBytes();
if(noOfNextBytes > remaining())
{
invalidateNoOfNextBytes();
return false;
}
if(getString() == s)
{
return true;
}
else
{
(*this) -= noOfNextBytes;
return false;
}
}
bool Bytestream::nextBytestream(const Bytestream& other)
{
if(_noOfNextBytesValid && getNoOfNextBytes() != other.size())
{
throw logic_error("Desired length does not match const length");
}
else if(!_noOfNextBytesValid)
{
setNoOfNextBytes(other.size());
}
size_t noOfNextBytes = getNoOfNextBytes();
if(noOfNextBytes > remaining())
{
invalidateNoOfNextBytes();
return false;
}
if(getBytestream() == other)
{
return true;
}
else
{
(*this) -= noOfNextBytes;
return false;
}
}
#define PUT(type, shorthand, len) PUT_(type##len##_t, shorthand##len, len)
#define PUT_(type, shortType, len) \
void Bytestream::put##shortType(type u) \
{if(needsSwap()){u=bswap(u);} \
putBytes(&u, sizeof(u));}
PUT(uint, U, 8)
PUT(uint, U, 16)
PUT(uint, U, 32)
PUT(uint, U, 64)
PUT(int, S, 8)
PUT(int, S, 16)
PUT(int, S, 32)
PUT(int, S, 64)
PUT(float, F, 32)
PUT(float, F, 64)
void Bytestream::putString(const std::string& s)
{
putBytes(s.c_str(), s.length());
}
void Bytestream::putBytestream(const Bytestream& other)
{
putBytes(other.raw(), other.size());
}
void Bytestream::putBytes(const void* c, size_t len)
{
uint8_t* old = _data;
_data = new uint8_t[_size+len];
if (_size != 0)
{
memcpy(_data, old, _size);
delete old;
}
memcpy((_data+_size), c, len);
_size += len;
}
void Bytestream::setNoOfNextBytes(size_t n)
{
_noOfNextBytes = n;
_noOfNextBytesValid = true;
}
void Bytestream::invalidateNoOfNextBytes()
{
_noOfNextBytes = 0;
_noOfNextBytesValid = false;
}
void Bytestream::_before(size_t bytesToRead)
{
if(bytesToRead > remaining())
{
invalidateNoOfNextBytes();
throw out_of_range("Tried to read past end");
}
}
void Bytestream::_after(size_t bytesRead)
{
_pos += bytesRead;
_noOfNextBytesValid = false;
}
Bytestream Bytestream::operator[](size_t i)
{
Bytestream tmp(_data+i, _size-i);
return tmp;
}
Bytestream& Bytestream::operator+=(size_t i)
{
if((_pos+i) > _size)
{
invalidateNoOfNextBytes();
throw out_of_range("Tried to address data past end");
}
_pos += i;
return *this;
}
Bytestream& Bytestream::operator-=(size_t i)
{
_pos -= i;
return *this;
}
Bytestream& Bytestream::operator/(int i)
{
setNoOfNextBytes(i);
return *this;
}
#define PUTOP(type, shorthand, len) PUTOP_(type##len##_t, shorthand##len)
#define PUTOP_(type, shortType) \
Bytestream& Bytestream::operator<<(const type& u) \
{put##shortType(u); return *this;}
PUTOP(uint, U, 8)
PUTOP(uint, U, 16)
PUTOP(uint, U, 32)
PUTOP(uint, U, 64)
PUTOP(int, S, 8)
PUTOP(int, S, 16)
PUTOP(int, S, 32)
PUTOP(int, S, 64)
PUTOP(float, F, 32)
PUTOP(float, F, 64)
Bytestream& Bytestream::operator<<(const std::string& s)
{
putString(s);
return *this;
}
Bytestream& Bytestream::operator<<(const Bytestream& other)
{
putBytestream(other);
return *this;
}
#define GETOP(type, shorthand, len) GETOP_(type##len##_t, shorthand##len)
#define GETOP_(type, shortType) \
Bytestream& Bytestream::operator>>(type& u) \
{u = get##shortType(); return *this;}
GETOP(uint, U, 8)
GETOP(uint, U, 16)
GETOP(uint, U, 32)
GETOP(uint, U, 64)
GETOP(int, S, 8)
GETOP(int, S, 16)
GETOP(int, S, 32)
GETOP(int, S, 64)
GETOP(float, F, 32)
GETOP(float, F, 64)
Bytestream& Bytestream::operator>>(std::string& s)
{
s = getString();
return *this;
}
Bytestream& Bytestream::operator>>(Bytestream& other)
{
other = getBytestream();
return *this;
}
#define GETOP_CONST(type, shorthand, len) \
GETOP_CONST_(type##len##_t, shorthand##len)
#define GETOP_CONST_(type, shortType) \
Bytestream& Bytestream::operator>>(const type& u) \
{type v = get##shortType();\
if(u!=v) {(*this) -= sizeof(type);\
throw Badmatch("Does not match const", v, u);}\
else{return *this;}}
GETOP_CONST(uint, U, 8)
GETOP_CONST(uint, U, 16)
GETOP_CONST(uint, U, 32)
GETOP_CONST(uint, U, 64)
GETOP_CONST(int, S, 8)
GETOP_CONST(int, S, 16)
GETOP_CONST(int, S, 32)
GETOP_CONST(int, S, 64)
GETOP_CONST(float, F, 32)
GETOP_CONST(float, F, 64)
Bytestream& Bytestream::operator>>(const std::string& s)
{
if (_noOfNextBytesValid && getNoOfNextBytes() != s.length())
{
throw logic_error("Desired length does not match const length");
}
else if(!_noOfNextBytesValid)
{
setNoOfNextBytes(s.length());
}
std::string sv = getString();
if(sv != s)
{
(*this) -= s.length();
throw Badmatch("Does not match const", sv, s);
}
return *this;
}
#define NEXTOP(type, shorthand, len) NEXTOP_(type##len##_t, shorthand##len)
#define NEXTOP_(type, shortType) \
bool Bytestream::operator>>=(const type& u) \
{return next##shortType(u);}
NEXTOP(uint, U, 8)
NEXTOP(uint, U, 16)
NEXTOP(uint, U, 32)
NEXTOP(uint, U, 64)
NEXTOP(int, S, 8)
NEXTOP(int, S, 16)
NEXTOP(int, S, 32)
NEXTOP(int, S, 64)
NEXTOP(float, F, 32)
NEXTOP(float, F, 64)
bool Bytestream::operator>>=(const std::string& s)
{
return nextString(s);
}
bool Bytestream::operator>>=(const Bytestream& other)
{
return nextBytestream(other);
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
bool Bytestream::needsSwap()
{
return _endianness != Endianness::NativeEndian
&& _endianness != Endianness::LittleEndian;
}
#elif __BYTE_ORDER == __BIG_ENDIAN
bool Bytestream::needsSwap()
{
return _endianness != Endianness::NativeEndian
&& _endianness != Endianness::BigEndian;
}
#else
#error
#endif

189
src/bytestream.h Normal file
View file

@ -0,0 +1,189 @@
#ifndef BYTESTREAM_H
#define BYTESTREAM_H
#include <string>
#include <stdexcept>
#include <byteswap.h>
#ifndef __STDC_IEC_559__
#error "Double must be IEEE 754"
#endif
#define float32_t float
#define float64_t double
class Bytestream
{
public:
class Badmatch : public std::invalid_argument::invalid_argument
{
public:
Badmatch(std::string s, std::string v, std::string u) :
invalid_argument(s+": "+v+" != "+u) {}
template<typename T>
Badmatch(std::string s, T v, T u) :
invalid_argument(s+": "+std::to_string(v)+" != "+std::to_string(u)) {}
};
enum Endianness {
NativeEndian,
BigEndian,
LittleEndian
};
Bytestream();
Bytestream(size_t len);
Bytestream(const void* data, size_t len);
Bytestream(const void* data, size_t len, Endianness e);
Bytestream(const Bytestream& rhs);
~Bytestream();
bool operator==(const Bytestream& other) const;
bool operator!=(const Bytestream& other) const;
Bytestream& operator=(const Bytestream& other);
uint8_t* raw() const {return _data;}
size_t size() const {return _size;}
size_t pos() const {return _pos;}
size_t remaining() const {return _size - _pos;}
bool atEnd() const {return _pos >= _size;}
void setPos(size_t pos) {_pos = pos;}
Endianness getEndianness() {return _endianness;}
void setEndianness(Endianness e) {_endianness = e;}
uint8_t getU8();
uint16_t getU16();
uint32_t getU32();
uint64_t getU64();
int8_t getS8();
int16_t getS16();
int32_t getS32();
int64_t getS64();
float32_t getF32();
float64_t getF64();
std::string getString();
Bytestream getBytestream();
std::string getString(size_t len);
Bytestream getBytestream(size_t len);
void getBytes(void* cs, size_t len);
uint8_t peekU8();
uint16_t peekU16();
uint32_t peekU32();
uint64_t peekU64();
int8_t peekS8();
int16_t peekS16();
int32_t peekS32();
int64_t peekS64();
float32_t peekF32();
float64_t peekF64();
std::string peekString();
Bytestream peekBytestream();
std::string peekString(size_t len);
Bytestream peekBytestream(size_t len);
bool nextU8(uint8_t);
bool nextU16(uint16_t);
bool nextU32(uint32_t);
bool nextU64(uint64_t);
bool nextS8(int8_t);
bool nextS16(int16_t);
bool nextS32(int32_t);
bool nextS64(int64_t);
bool nextF32(float32_t);
bool nextF64(float64_t);
bool nextString(const std::string& bts);
bool nextBytestream(const Bytestream& bts);
void putU8(uint8_t);
void putU16(uint16_t);
void putU32(uint32_t);
void putU64(uint64_t);
void putS8(int8_t);
void putS16(int16_t);
void putS32(int32_t);
void putS64(int64_t);
void putF32(float32_t);
void putF64(float64_t);
void putString(const std::string&);
void putBytestream(const Bytestream&);
void putBytes(const void* c, size_t len);
void setNoOfNextBytes(size_t n);
void invalidateNoOfNextBytes();
size_t getNoOfNextBytes() {return _noOfNextBytes;}
bool noOfNextBytesValid() const {return _noOfNextBytesValid;}
Bytestream operator[](size_t i);
Bytestream& operator+=(size_t i);
Bytestream& operator-=(size_t i);
Bytestream& operator/(int i);
Bytestream& operator<<(const uint8_t& u);
Bytestream& operator<<(const uint16_t& u);
Bytestream& operator<<(const uint32_t& u);
Bytestream& operator<<(const uint64_t& u);
Bytestream& operator<<(const int8_t& u);
Bytestream& operator<<(const int16_t& u);
Bytestream& operator<<(const int32_t& u);
Bytestream& operator<<(const int64_t& u);
Bytestream& operator<<(const float32_t& u);
Bytestream& operator<<(const float64_t& u);
Bytestream& operator<<(const std::string& s);
Bytestream& operator<<(const Bytestream& other);
Bytestream& operator>>(uint8_t& u);
Bytestream& operator>>(uint16_t& u);
Bytestream& operator>>(uint32_t& u);
Bytestream& operator>>(uint64_t& u);
Bytestream& operator>>(int8_t& u);
Bytestream& operator>>(int16_t& u);
Bytestream& operator>>(int32_t& u);
Bytestream& operator>>(int64_t& u);
Bytestream& operator>>(float32_t& u);
Bytestream& operator>>(float64_t& u);
Bytestream& operator>>(std::string& s);
Bytestream& operator>>(Bytestream& other);
Bytestream& operator>>(const uint8_t& u);
Bytestream& operator>>(const uint16_t& u);
Bytestream& operator>>(const uint32_t& u);
Bytestream& operator>>(const uint64_t& u);
Bytestream& operator>>(const int8_t& u);
Bytestream& operator>>(const int16_t& u);
Bytestream& operator>>(const int32_t& u);
Bytestream& operator>>(const int64_t& u);
Bytestream& operator>>(const float32_t& u);
Bytestream& operator>>(const float64_t& u);
Bytestream& operator>>(const std::string& s);
bool operator>>=(const uint8_t& u);
bool operator>>=(const uint16_t& u);
bool operator>>=(const uint32_t& u);
bool operator>>=(const uint64_t& u);
bool operator>>=(const int8_t& u);
bool operator>>=(const int16_t& u);
bool operator>>=(const int32_t& u);
bool operator>>=(const int64_t& u);
bool operator>>=(const float32_t& u);
bool operator>>=(const float64_t& u);
bool operator>>=(const std::string& s);
bool operator>>=(const Bytestream& other);
private:
uint8_t* _data;
size_t _size;
size_t _pos;
size_t _noOfNextBytes;
bool _noOfNextBytesValid;
Endianness _endianness;
bool needsSwap();
void _after(size_t bytesRead);
void _before(size_t bytesToRead);
};
#endif

184
src/ippdiscovery.cpp Normal file
View file

@ -0,0 +1,184 @@
#include "ippdiscovery.h"
#define A 1
#define PTR 12
#define TXT 16
#define AAAA 28
#define SRV 33
void put_addr(Bytestream& bts, QStringList addr)
{
for(int i = 0; i < addr.length(); i++)
{
QString elem = addr[i];
bts << (quint8)elem.size() << elem.toStdString();
}
bts << (quint8)0;
}
QStringList get_addr(Bytestream& bts)
{
QStringList addr;
while(true)
{
if(bts.nextU8(0))
{
break;
}
else if ((bts.peekU8()&0xc0)==0xc0)
{
quint16 ref = bts.getU16() & 0x0fff;
Bytestream tmp = bts;
tmp.setPos(ref);
addr += get_addr(tmp);
break;
}
else
{
std::string elem;
bts/bts.getU8() >> elem;
addr.append(QString(elem.c_str()));
}
}
return addr;
}
IppDiscovery::IppDiscovery() : QStringListModel()
{
socket = new QUdpSocket(this);
connect(socket, SIGNAL(readyRead()),
this, SLOT(readPendingDatagrams()));
connect(this, SIGNAL(favouritesChanged()),
this, SLOT(update()));
}
IppDiscovery::~IppDiscovery() {
delete socket;
}
void IppDiscovery::discover() {
Bytestream query;
quint16 transactionid = 0;
quint16 flags = 0;
quint16 questions = 1;
query << transactionid << flags << questions << (quint16)0 << (quint16)0 << (quint16)0;
put_addr(query, {"_ipp","_tcp","local"});
query << (quint16)0x000C << (quint16)0x0001;
QByteArray bytes((char*)(query.raw()), query.size());
socket->writeDatagram(bytes, QHostAddress("224.0.0.251"), 5353);
}
void IppDiscovery::update()
{
qDebug() << _favourites << _found;
this->setStringList(_favourites+_found);
}
void IppDiscovery::readPendingDatagrams()
{
while (socket->hasPendingDatagrams()) {
size_t size = socket->pendingDatagramSize();
Bytestream resp(size);
QHostAddress sender;
quint16 senderPort;
QMap<QString,QString> ptrs;
QMap<QString,QString> rps;
QMap<QString,quint16> ports;
QMap<QString,QString> targets;
QMultiMap<QString,QString> AAs;
QMultiMap<QString,QString> AAAAs;
socket->readDatagram((char*)(resp.raw()), size, &sender, &senderPort);
sender = QHostAddress(sender.toIPv4Address());
quint16 transactionid, flags, questions, answerRRs, authRRs, addRRs;
resp >> transactionid >> flags >> questions >> answerRRs >> authRRs >> addRRs;
for(quint16 i = 0; i < questions; i++)
{
quint16 qtype, qflags;
QString qaddr = get_addr(resp).join('.');
resp >> qtype >> qflags;
}
for(quint16 i = 0; i < answerRRs; i++)
{
quint16 atype, aflags, len;
quint32 ttl;
QString aaddr = get_addr(resp).join('.');
resp >> atype >> aflags >> ttl >> len;
quint16 pos_before = resp.pos();
if (atype == PTR)
{
QString tmpname = get_addr(resp).join(".");
ptrs[aaddr] = tmpname;
}
else if(atype == TXT)
{
Bytestream tmp;
while(resp.pos() < pos_before+len)
{
resp/resp.getU8() >> tmp;
if(tmp >>= "rp=")
{
std::string tmprp;
tmp/tmp.remaining() >> tmprp;
rps[aaddr] = tmprp.c_str();
}
}
}
else if (atype == SRV)
{
quint16 prio, w, port;
resp >> prio >> w >> port;
QString target = get_addr(resp).join(".");
ports[aaddr] = port;
targets[aaddr] = target;
}
else if(atype == A)
{
quint32 addr;
resp >> addr;
QHostAddress haddr(addr);
AAs.insert(aaddr, haddr.toString());
}
else
{
resp += len;
}
Q_ASSERT(resp.pos() == pos_before+len);
}
for(QMap<QString,QString>::Iterator it = ptrs.begin(); it != ptrs.end(); it++)
{
quint16 port = ports[it.value()];
QString target = targets[it.value()];
QString rp = rps[it.value()];
for(QMultiMap<QString,QString>::Iterator ait = AAs.begin(); ait != AAs.end(); ait++)
{
if(ait.key() == target)
{
QString ip = ait.value();
QString addr = ip+":"+QString::number(port)+"/"+rp;
if(!_found.contains(addr))
{
_found.append(addr);
_found.sort(Qt::CaseInsensitive);
}
}
}
}
}
this->update();
}

29
src/ippdiscovery.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef IPPDISCOVERY_H
#define IPPDISCOVERY_H
#include <QStringListModel>
#include <QUdpSocket>
#include "bytestream.h"
class IppDiscovery : public QStringListModel
{
Q_OBJECT
public:
IppDiscovery();
~IppDiscovery();
Q_PROPERTY(QStringList favourites MEMBER _favourites NOTIFY favouritesChanged)
Q_INVOKABLE void discover();
signals:
void favouritesChanged();
public slots:
void readPendingDatagrams();
void update();
protected:
private:
QStringList _favourites;
QStringList _found;
QUdpSocket* socket;
};
#endif // IPPDISCOVERY_H

278
src/ippmsg.cpp Normal file
View file

@ -0,0 +1,278 @@
#include "ippmsg.h"
#define MAJ_VSN 1
#define MIN_VSN 1
quint32 IppMsg::_reqid=1;
IppMsg::IppMsg()
{
}
IppMsg::IppMsg(QJsonObject opAttrs, QJsonObject jobAttrs)
{
_opAttrs = opAttrs;
_jobAttrs = jobAttrs;
}
IppMsg::~IppMsg()
{
}
IppMsg::IppMsg(QNetworkReply* resp)
{
QByteArray tmp = resp->readAll();
Bytestream bts(tmp.constData(), tmp.length());
quint8 majVsn;
quint8 minVsn;
quint16 status;
quint32 reqId;
bts >> majVsn >> minVsn >> status >> reqId;
QJsonObject* attrs = 0;
QString last_name;
while(!bts.atEnd())
{
if(bts>>=(quint8)IppTag::OpAttrs)
{
attrs = &_opAttrs;
}
else if (bts>>=(quint8)IppTag::JobAttrs) {
attrs = &_jobAttrs;
}
else if (bts>>=(quint8)IppTag::EndAttrs) {
break;
}
else if (bts>>=(quint8)IppTag::PrinterAttrs) {
attrs = &_printerAttrs;
}
else {
last_name = consume_attribute(attrs, bts, last_name);
}
}
}
QString IppMsg::consume_attribute(QJsonObject* attrs, Bytestream& data, QString lastName)
{
quint8 tag;
quint16 tmp_len;
QString name;
QJsonValue value;
std::string tmp_str = "";
bool noList = false;
data >> tag >> tmp_len;
data/tmp_len >> tmp_str;
name = tmp_str!="" ? tmp_str.c_str() : lastName;
switch (tag) {
case OpAttrs:
case JobAttrs:
case EndAttrs:
case PrinterAttrs:
Q_ASSERT(false);
case Integer:
case Enum:
quint32 tmp_u32;
data >> tmp_len >> tmp_u32;
value = (int)tmp_u32;
break;
case Boolean:
quint8 tmp_bool;
data >> tmp_len >> tmp_bool;
value = (bool)tmp_bool;
noList = true;
break;
case DateTime:
{
quint16 year;
quint8 month, day, hour, minutes, seconds, deci_seconds,
plus_minus, utc_h_offset, utc_m_offset;
data >> tmp_len >> year >> month >> day >> hour >> minutes >> seconds >> deci_seconds
>> plus_minus >> utc_h_offset >> utc_m_offset;
QDate date(year, month, day);
QTime time(hour, minutes, seconds, deci_seconds*100);
int offset_seconds = (plus_minus == '+' ? 1 : -1)*(utc_h_offset*60*60+utc_m_offset*60);
value = QDateTime(date, time, Qt::OffsetFromUTC, offset_seconds).toString(Qt::ISODate);
break;
}
case Resolution:
{
qint32 x, y;
qint8 units;
QJsonObject tmp_res;
data >> tmp_len >> x >> y >> units;
tmp_res.insert("x", x);
tmp_res.insert("y", y);
tmp_res.insert("units", units);
value = tmp_res;
break;
}
case IntegerRange:
{
qint32 low, high;
data >> tmp_len >> low >> high;
QJsonObject tmp_range;
tmp_range.insert("low", low);
tmp_range.insert("high", high);
value = tmp_range;
noList = true;
break;
}
case OctetStringUnknown:
case TextWithLanguage:
case NameWithLanguage:
case TextWithoutLanguage:
case NameWithoutLanguage:
case Keyword:
case Uri:
case UriScheme:
case Charset:
case NaturalLanguage:
case MimeMediaType:
default:
data >> tmp_len;
data/tmp_len >> tmp_str;
value = tmp_str.c_str();
break;
};
if(attrs->contains(name))
{
QJsonObject tmp = (*attrs)[name].toObject();
QJsonArray tmpa;
if(tmp["value"].isArray())
{
tmpa = tmp["value"].toArray();
}
else
{
tmpa = QJsonArray {tmp["value"]};
}
tmpa.append(value);
tmp["value"] = tmpa;
attrs->insert(name, tmp);
}
else
{
if((name.endsWith("-supported") || name == "printer-icons") && !noList)
{
value = QJsonArray {value};
}
attrs->insert(name, QJsonObject {{"tag", tag}, {"value", value}});
}
return name;
}
QByteArray IppMsg::encode(Operation op)
{
Bytestream ipp;
ipp << quint8(MAJ_VSN) << quint8(MIN_VSN);
ipp << quint16(op);
ipp << _reqid++;
if(!_opAttrs.empty())
{
ipp << quint8(1);
for(QJsonObject::iterator it = _opAttrs.begin(); it != _opAttrs.end(); it++)
{
QJsonObject val = it.value().toObject();
ipp << encode_attr(val["tag"].toInt(), it.key(), val["value"]);
}
}
if(!_jobAttrs.empty())
{
ipp << quint8(2);
for(QJsonObject::iterator it = _jobAttrs.begin(); it != _jobAttrs.end(); it++)
{
QJsonObject val = it.value().toObject();
ipp << encode_attr(val["tag"].toInt(), it.key(), val["value"]);
}
}
ipp << quint8(3);
return QByteArray((char*)(ipp.raw()), ipp.size());
}
Bytestream IppMsg::encode_attr(quint8 tag, QString name, QJsonValueRef value)
{
Bytestream req;
switch (tag) {
case OpAttrs:
case JobAttrs:
case EndAttrs:
case PrinterAttrs:
Q_ASSERT(false);
case Integer:
case Enum:
{
quint32 tmp_u32 = value.toInt();
req << (quint16)4 << tmp_u32;
break;
}
case Boolean:
{
quint32 tmp_u8 = value.toBool();
req << (quint16)1 << tmp_u8;
break;
}
case DateTime:
{
Q_ASSERT("fixme");
break;
}
case Resolution:
{
qDebug() << value << value.toObject();
qint32 x = value.toObject()["x"].toInt();
qint32 y = value.toObject()["y"].toInt();
qint8 units = value.toObject()["units"].toInt();
req << (quint16)9 << x << y << units;
break;
}
case IntegerRange:
{
qint32 low = value.toObject()["low"].toInt();
qint32 high = value.toObject()["high"].toInt();
req << (quint16)8 << low << high;
break;
}
case OctetStringUnknown:
case TextWithLanguage:
case NameWithLanguage:
case TextWithoutLanguage:
case NameWithoutLanguage:
case Keyword:
case Uri:
case UriScheme:
case Charset:
case NaturalLanguage:
case MimeMediaType:
req << quint16(value.toString().length()) << value.toString().toStdString();
break;
default:
qDebug() << "uncaught tag" << tag;
Q_ASSERT(false);
break;
}
Bytestream actual;
if(req.size() != 0)
{
actual << tag << quint16(name.length()) << name.toStdString() << req;
}
return actual;
}

84
src/ippmsg.h Normal file
View file

@ -0,0 +1,84 @@
#ifndef IPP_PROTO_H
#define IPP_PROTO_H
#include "bytestream.h"
#include <QByteArray>
#include <QDebug>
#include <QUrl>
#include <QtNetwork>
#include <QNetworkReply>
#include <QIODevice>
class IppMsg
{
public:
enum IppTag : quint8 {
OpAttrs = 0x01,
JobAttrs = 0x02,
EndAttrs = 0x03,
PrinterAttrs = 0x04,
Integer = 0x21,
Boolean = 0x22,
Enum = 0x23,
OctetStringUnknown = 0x30,
DateTime = 0x31,
Resolution = 0x32,
IntegerRange = 0x33,
TextWithLanguage = 0x35,
NameWithLanguage = 0x36,
TextWithoutLanguage = 0x41,
NameWithoutLanguage = 0x42,
Keyword = 0x44,
Uri = 0x45,
UriScheme = 0x46,
Charset = 0x47,
NaturalLanguage = 0x48,
MimeMediaType = 0x49
};
enum Operation : quint16 {
PrintJob = 0x0002,
PrintUri = 0x0003,
ValidateJob = 0x0004,
CreateJob = 0x0005,
SendDocument = 0x0006,
SendUri = 0x0007,
CancelJob = 0x0008,
GetJobAttrs = 0x0009,
GetJobs = 0x000A,
GetPrinterAttrs = 0x000B,
HoldJob = 0x000C,
ReleaseJob = 0x000D,
RestartJob = 0x000E,
PausePrinter = 0x0010,
ResumePrinter = 0x0011,
PurgeJobs = 0x0012
};
IppMsg();
IppMsg(QNetworkReply* resp);
IppMsg(QJsonObject opAttrs, QJsonObject jobAttrs = QJsonObject());
IppMsg(const IppMsg& other) = default;
~IppMsg();
QByteArray encode(Operation op);
QJsonObject getPrinterAttrs() {return _printerAttrs;}
QJsonObject getJobAttrs() {return _jobAttrs;}
QJsonObject getOpAttrs() {return _opAttrs;}
protected:
private:
QString consume_attribute(QJsonObject* attrs, Bytestream& data, QString lastName);
Bytestream encode_attr(quint8 tag, QString name, QJsonValueRef value);
QJsonObject _opAttrs;
QJsonObject _jobAttrs;
QJsonObject _printerAttrs;
static quint32 _reqid;
};
#endif // IPP_PROTO_H

120
src/ippprinter.cpp Normal file
View file

@ -0,0 +1,120 @@
#include "ippprinter.h"
IppPrinter::IppPrinter()
{
_nam = new QNetworkAccessManager(this);
_jnam = new QNetworkAccessManager(this);
connect(_nam, SIGNAL(finished(QNetworkReply*)),this, SLOT(getPrinterAttributesFinished(QNetworkReply*)));
connect(_jnam, SIGNAL(finished(QNetworkReply*)),this, SLOT(jobRequestFinished(QNetworkReply*)));
QObject::connect(this, &IppPrinter::urlChanged, this, &IppPrinter::onUrlChanged);
}
IppPrinter::~IppPrinter() {
delete _nam;
delete _jnam;
}
void IppPrinter::setUrl(QString url)
{
if(url != _url)
{
_url = url;
emit urlChanged();
}
}
void IppPrinter::onUrlChanged()
{
_attrs = QJsonObject();
emit attrsChanged();
QNetworkRequest request;
QUrl url("http://"+_url);
qDebug() << _url << url.port();
if(url.port() == -1) {
url.setPort(631);
}
request.setUrl(url);
// request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/ipp");
QJsonObject o
{
{"attributes-charset", QJsonObject {{"tag", IppMsg::Charset}, {"value", "utf-8"}}},
{"attributes-natural-language", QJsonObject {{"tag", IppMsg::NaturalLanguage}, {"value", "en-us"}}},
{"printer-uri", QJsonObject {{"tag", IppMsg::Uri}, {"value", "ipp://"+_url}}},
{"requesting-user-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", "nemo"}}}
};
IppMsg msg = IppMsg(o);
_nam->post(request, msg.encode(IppMsg::GetPrinterAttrs));
}
void IppPrinter::getPrinterAttributesFinished(QNetworkReply *reply)
{
if(reply->error() == QNetworkReply::NoError)
{
try {
IppMsg resp(reply);
_attrs = resp.getPrinterAttrs();
emit attrsChanged();
}
catch(std::exception e)
{
qDebug() << e.what();
}
}
}
void IppPrinter::jobRequestFinished(QNetworkReply *reply)
{
if(reply->error() == QNetworkReply::NoError)
{
try {
IppMsg resp(reply);
qDebug() << resp.getOpAttrs() << resp.getJobAttrs();
_jobAttrs = resp.getJobAttrs();
emit jobAttrsChanged();
}
catch(std::exception e)
{
qDebug() << e.what();
}
}
}
bool IppPrinter::print(QJsonObject attrs, QString filename){
qDebug() << "printing" << filename << attrs;
QFile file(filename);
bool file_ok = file.open(QIODevice::ReadOnly);
if(!file_ok)
return false;
QFileInfo fileinfo(file);
QNetworkRequest request;
QUrl url("http://"+_url);
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/ipp");
QJsonObject o
{
{"attributes-charset", QJsonObject {{"tag", IppMsg::Charset}, {"value", "utf-8"}}},
{"attributes-natural-language", QJsonObject {{"tag", IppMsg::NaturalLanguage}, {"value", "en-us"}}},
{"printer-uri", QJsonObject {{"tag", IppMsg::Uri}, {"value", "ipp://"+_url}}},
{"requesting-user-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", "nemo"}}},
{"job-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", fileinfo.fileName()}}},
};
IppMsg job = IppMsg(o, attrs);
QByteArray contents = job.encode(IppMsg::PrintJob);
QByteArray filedata = file.readAll();
contents = contents.append(filedata);
_jnam->post(request, contents);
file.close();
return true;
}

46
src/ippprinter.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef IPPPRINTER_H
#define IPPPRINTER_H
#include <QtNetwork>
#include <QNetworkAccessManager>
#include "ippmsg.h"
class IppPrinter : public QObject
{
Q_OBJECT
Q_PROPERTY(QString url READ getUrl WRITE setUrl NOTIFY urlChanged)
Q_PROPERTY(QJsonObject attrs MEMBER _attrs NOTIFY attrsChanged)
Q_PROPERTY(QJsonObject jobAttrs MEMBER _jobAttrs NOTIFY jobAttrsChanged)
public:
IppPrinter();
~IppPrinter();
QString getUrl() {return _url;}
void setUrl(QString url);
Q_INVOKABLE bool print(QJsonObject attrs, QString file);
signals:
void urlChanged();
void attrsChanged();
void jobAttrsChanged();
public slots:
void onUrlChanged();
void getPrinterAttributesFinished(QNetworkReply* reply);
void jobRequestFinished(QNetworkReply* reply);
private:
QString _url;
QNetworkAccessManager* _nam;
QNetworkAccessManager* _jnam;
QJsonObject _attrs;
QJsonObject _jobAttrs;
};
#endif // IPPPRINTER_H