Actually add old project files
This commit is contained in:
parent
c5ef6a6982
commit
210e40920c
18 changed files with 2047 additions and 0 deletions
70
qml/pages/ChoiceSetting.qml
Normal file
70
qml/pages/ChoiceSetting.qml
Normal 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
52
qml/pages/InputDialog.qml
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
43
qml/pages/IntegerInputDialog.qml
Normal file
43
qml/pages/IntegerInputDialog.qml
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
50
qml/pages/IntegerSetting.qml
Normal file
50
qml/pages/IntegerSetting.qml
Normal 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
130
qml/pages/PrinterPage.qml
Normal 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 {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
30
qml/pages/RangeSetting.qml
Normal file
30
qml/pages/RangeSetting.qml
Normal 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
21
qml/pages/Setting.qml
Normal 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
56
qml/pages/printer.svg
Normal 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
45
qml/pages/utils.js
Normal 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
67
rpm/harbour-seaprint.spec
Normal 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
553
src/bytestream.cpp
Normal 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
189
src/bytestream.h
Normal 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
184
src/ippdiscovery.cpp
Normal 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
29
src/ippdiscovery.h
Normal 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
278
src/ippmsg.cpp
Normal 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
84
src/ippmsg.h
Normal 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
120
src/ippprinter.cpp
Normal 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
46
src/ippprinter.h
Normal 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
|
Loading…
Reference in a new issue