Compare commits
135 commits
upgrade-4.
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
38975d1f64 | ||
|
53f458d71a | ||
|
f138f0b89d | ||
|
1d71f3477a | ||
|
2d0d61f3e5 | ||
|
1905cec933 | ||
|
912bc36dad | ||
|
317a864ff6 | ||
|
d473d23d55 | ||
|
29c5bbf62b | ||
|
e4038db1b0 | ||
|
fce00048ef | ||
|
62bcecccbe | ||
|
d3bc294f0a | ||
|
5d5e86dc6b | ||
|
3045244c71 | ||
|
3aad66c714 | ||
|
31bfd08fae | ||
|
6f4d602456 | ||
|
3d8ade0bef | ||
|
7a144471e3 | ||
|
5433d81764 | ||
|
a51061df7c | ||
|
741bdd7724 | ||
|
f9b740c94b | ||
|
371c06b430 | ||
|
38d5a95982 | ||
|
ac84fe079e | ||
|
53ca370f92 | ||
|
3abd1e103f | ||
|
8712ee6eea | ||
|
9287da53aa | ||
|
6a06fa5ffd | ||
|
b7aeb097a3 | ||
|
b70be18897 | ||
|
15cae28413 | ||
|
5f71f2dd64 | ||
|
51c90e6443 | ||
|
d037600d0f | ||
|
d094db4d89 | ||
|
515d3944e7 | ||
|
31cdaf0939 | ||
|
2cd0bb2812 | ||
|
445cc19b10 | ||
|
56395707fa | ||
|
aaae57b113 | ||
|
da923e3a32 | ||
|
071c39b6f3 | ||
|
04830863ab | ||
|
95ef563230 | ||
|
8ea54769c5 | ||
|
f6ec27b423 | ||
|
0874122bda | ||
|
813e1edb6a | ||
|
c30e7d3548 | ||
|
fb40a89858 | ||
|
64a7b0120f | ||
|
8c6344eef8 | ||
|
9ff8775fab | ||
|
6c7e09fdb5 | ||
|
1e6be94965 | ||
|
342a1a82f0 | ||
|
58619bd63a | ||
|
a7d181a867 | ||
|
18f0c66dba | ||
|
ab96dc547f | ||
|
6ba065b4b1 | ||
|
ba2a826b56 | ||
|
21b3819a86 | ||
|
08b66ed711 | ||
|
ff2eae04cf | ||
|
b4aaa85a2d | ||
|
f30f40683e | ||
|
f84d05e514 | ||
|
dd19333c81 | ||
|
b1cd524b4c | ||
|
0dd09a2707 | ||
|
6e2ded84e2 | ||
|
5799985c92 | ||
|
cecc038a6d | ||
|
993eb75cf7 | ||
|
ea39f0285b | ||
|
438628598f | ||
|
09ceb4f0ec | ||
|
e16bfbe7c6 | ||
|
ef3b53abdb | ||
|
bb6fa7926d | ||
|
40da0790e2 | ||
|
0312a367f4 | ||
|
71b2986a37 | ||
|
3be30e58e6 | ||
|
444cf09ae0 | ||
|
0efd96c072 | ||
|
d8267db9fe | ||
|
4537f32005 | ||
|
9ba3f2daf1 | ||
|
4f06a8e85e | ||
|
595a253693 | ||
|
23fe3741f9 | ||
|
8ae48b83e6 | ||
|
afb8d65ea8 | ||
|
390e5dd16d | ||
|
40e7f32846 | ||
|
263090a518 | ||
|
3bcba3b8d7 | ||
|
8843275fde | ||
|
8b4c2f373b | ||
|
2ab6a7d979 | ||
|
0684e677ec | ||
|
6c4e48d69c | ||
|
47cde78edc | ||
|
cd90064f9a | ||
|
9da439e463 | ||
|
6095694d92 | ||
|
4c0a7ed9fd | ||
|
02be96f090 | ||
|
d6db1ff5e2 | ||
|
c93d4a019f | ||
|
71dd1452bc | ||
|
4e5967e36d | ||
|
6ee912680e | ||
|
b6288ca1ee | ||
|
d20c115747 | ||
|
47149e0cd7 | ||
|
a78e2c4449 | ||
|
47fb9c8555 | ||
|
9b6fd90ed2 | ||
|
0da1783c51 | ||
|
cd62f76fc2 | ||
|
96d7fd8772 | ||
|
171dd994c8 | ||
|
76b219dc7c | ||
|
1e93735a4e | ||
|
70206916e4 | ||
|
79e7175f87 |
72 changed files with 38283 additions and 4572 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -41,7 +41,3 @@ target_wrapper.*
|
|||
|
||||
# QtCreator CMake
|
||||
CMakeLists.txt.user*
|
||||
|
||||
# Automatically generated
|
||||
|
||||
seaprint_version.h
|
||||
|
|
33
README.md
33
README.md
|
@ -11,7 +11,7 @@ A minority of printers support only PCL and/or their own proprietary formats, th
|
|||
|
||||
## Format support
|
||||
|
||||
For PDFs or plaintext, your printer needs to support one of the following formats:
|
||||
For PDFs, your printer needs to support one of the following formats:
|
||||
* PDF
|
||||
* Postscript
|
||||
* PWG-raster
|
||||
|
@ -19,6 +19,9 @@ For PDFs or plaintext, your printer needs to support one of the following format
|
|||
|
||||
For Postscript, your printer needs to support Postscript natively. (SeaPrint just sends it as-is)
|
||||
|
||||
For Plaintext, SeaPrint can render it to PDF internally and then the requirements follow those of PDF.
|
||||
You can also send Plaintext to supporting printers and leave the rendering up to the printer (SeaPrint will strip leading and trailing Form Feed characters and ensure CR+LF newlines, but not do any reflow or similar). Since rendering will differ between printers, this has lowest priority for automatic format selection.
|
||||
|
||||
For printing "Office" files, like .doc(x) and odt, you need to install
|
||||
`calligraconverter` (from the `calligra` package in Sailfish OS 4.2 onwards).
|
||||
Then the printer support follows that of PDF.
|
||||
|
@ -31,8 +34,18 @@ For images, your printer needs to support one of the following formats:
|
|||
* PDF
|
||||
* Postscript
|
||||
|
||||
For printing JPEG images, transferring the unaltered image has priority and the printer does the scaling.
|
||||
Other image formats are lossless and SeaPrint does scale-and-rotate to fit, so JPEG has lowest prio there.
|
||||
When printing images the result may depend on how they are transferred.
|
||||
If a document-oriented format like PDF/Postscript/PWG/URF is used SeaPrint does scale-and-rotate to fit, otherwise (when transferred as PNG or JPEG) that is left up to the printer. Only when printing images *as images* can the scaling setting be used.
|
||||
|
||||
For printing JPEG images, transferring them as JPEG has highest priority.
|
||||
JPEGs are then (losslessly) re-encoded to the baseline encoding profile to work with all printers (since SeaPrint 1.1).
|
||||
For other image formats JPEG has lowest priority as transfer format since it is lossy.
|
||||
|
||||
![Format flowchart](doc/pipeline.svg)
|
||||
|
||||
Flowchart made with [GoAT](https://github.com/blampe/goat).
|
||||
|
||||
pdf2printable, ppm2pwg and baselinify were made especially for SeaPrint and can be found [here](https://github.com/attah/ppm2pwg).
|
||||
|
||||
Printers with any of these IPP certifications and derivative standards should likely be supported:
|
||||
|
||||
|
@ -57,7 +70,7 @@ Create the file `/home/$USER/.config/net.attah/seaprint/overrides`. (NB: New loc
|
|||
This file should contain a JSON structure, with the outermost key(s) being "what attribute to match on" (uuid recommended if available), the next level is "if this value matches". Everything beneath that is what to inject/replace into the printer's attributes (see debug info above).
|
||||
|
||||
Example:
|
||||
```
|
||||
```JSON
|
||||
{
|
||||
"printer-uuid": {
|
||||
"urn:uuid:xxx-yyy-zzz-111-1234567890": {
|
||||
|
@ -70,12 +83,18 @@ Example:
|
|||
"value": [
|
||||
"application/octet-stream", "application/pdf"
|
||||
]
|
||||
}
|
||||
},
|
||||
"some-other-attribute-name": {
|
||||
"tag": <some tag number>,
|
||||
"value": <some value>
|
||||
"tag": 42,
|
||||
"value": "some-value"
|
||||
}
|
||||
},
|
||||
"urn:uuid:other-printer-uuid": {
|
||||
...
|
||||
}
|
||||
},
|
||||
"other-attribute-to-match-other-printer-on": {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
27
doc/pipeline.goat
Normal file
27
doc/pipeline.goat
Normal file
|
@ -0,0 +1,27 @@
|
|||
Normal flow:
|
||||
.---------------.
|
||||
PDF -------------------------------------------->| +----------------------> PDF 1.5
|
||||
.-----------------. | |
|
||||
Plaintext ---->| QTextDocument +------------------>| pdf2printable +----------------------> Postscript 2
|
||||
'-----------------' | |
|
||||
DOC/PPT .-------------------. | (Poppler+Cairo) | .---------.
|
||||
ODT/ODP ---->| Calligraconverter +---------------->| | | |
|
||||
| (optional) | | +---->| +----> PWG-Raster
|
||||
'-------------------' .--------. | | | ppm2pwg |
|
||||
Images --------------------------->| +---->| | | +----> URF-Raster
|
||||
| | '---------------' | |
|
||||
| QImage +---------------------------->| |
|
||||
.--------------. | | '---------'
|
||||
SVG ---->| QSvgRenderer +---->| +----------------------------------------------> PNG, JPEG,
|
||||
'--------------' '--------' Reverse BMP
|
||||
|
||||
If target format is the same:
|
||||
|
||||
Postscript --------------------------------------------------------------------------------------> Postscript
|
||||
.-----------------------.
|
||||
Plaintext ---->| ensure CR+LF newlines +------------------------------------------------------> Plaintext
|
||||
'-----------------------'
|
||||
PNG --------------------------------------------------------------------------------------> PNG
|
||||
.------------.
|
||||
JPEG ---->| baselinify +-----------------------------------------------------------------> JPEG
|
||||
'------------' (Baseline profile)
|
444
doc/pipeline.svg
Normal file
444
doc/pipeline.svg
Normal file
|
@ -0,0 +1,444 @@
|
|||
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='441' width='976' font-family='Menlo,Lucida Console,monospace'>
|
||||
<style type="text/css">
|
||||
svg {
|
||||
color: #000000;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
svg {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<g transform='translate(8,16)'>
|
||||
<path d='M 496,16 L 608,16' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 120,32 L 472,32' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 624,32 L 808,32' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,48 L 304,48' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 120,64 L 152,64' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 320,64 L 472,64' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 624,64 L 808,64' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,80 L 304,80' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,96 L 320,96' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 688,96 L 752,96' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 120,112 L 152,112' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 336,112 L 472,112' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 624,128 L 664,128' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 768,128 L 808,128' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,144 L 320,144' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 360,144 L 416,144' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 120,160 L 336,160' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 432,160 L 472,160' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 768,160 L 808,160' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 496,176 L 608,176' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 432,192 L 664,192' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,208 L 280,208' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 688,208 L 752,208' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 120,224 L 152,224' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 296,224 L 336,224' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 432,224 L 808,224' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,240 L 280,240' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 360,240 L 416,240' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 120,304 L 808,304' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,320 L 352,320' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 120,336 L 152,336' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 368,336 L 808,336' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,352 L 352,352' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 120,368 L 808,368' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,384 L 264,384' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 120,400 L 152,400' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 280,400 L 808,400' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,416 L 264,416' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 160,112 L 160,128' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 336,112 L 336,128' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 344,160 L 344,224' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 432,160 L 432,192' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 432,192 L 432,224' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 480,32 L 480,160' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 624,32 L 624,64' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 624,64 L 624,128' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 624,128 L 624,160' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 672,112 L 672,192' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 768,112 L 768,128' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 768,128 L 768,160' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 768,160 L 768,192' fill='none' stroke='currentColor'></path>
|
||||
<polygon points='160.000000,64.000000 148.000000,58.400002 148.000000,69.599998' fill='currentColor' transform='rotate(0.000000, 152.000000, 64.000000)'></polygon>
|
||||
<polygon points='160.000000,112.000000 148.000000,106.400002 148.000000,117.599998' fill='currentColor' transform='rotate(0.000000, 152.000000, 112.000000)'></polygon>
|
||||
<polygon points='160.000000,224.000000 148.000000,218.399994 148.000000,229.600006' fill='currentColor' transform='rotate(0.000000, 152.000000, 224.000000)'></polygon>
|
||||
<polygon points='160.000000,336.000000 148.000000,330.399994 148.000000,341.600006' fill='currentColor' transform='rotate(0.000000, 152.000000, 336.000000)'></polygon>
|
||||
<polygon points='160.000000,400.000000 148.000000,394.399994 148.000000,405.600006' fill='currentColor' transform='rotate(0.000000, 152.000000, 400.000000)'></polygon>
|
||||
<polygon points='344.000000,160.000000 332.000000,154.399994 332.000000,165.600006' fill='currentColor' transform='rotate(0.000000, 336.000000, 160.000000)'></polygon>
|
||||
<polygon points='344.000000,224.000000 332.000000,218.399994 332.000000,229.600006' fill='currentColor' transform='rotate(0.000000, 336.000000, 224.000000)'></polygon>
|
||||
<polygon points='480.000000,32.000000 468.000000,26.400000 468.000000,37.599998' fill='currentColor' transform='rotate(0.000000, 472.000000, 32.000000)'></polygon>
|
||||
<polygon points='480.000000,64.000000 468.000000,58.400002 468.000000,69.599998' fill='currentColor' transform='rotate(0.000000, 472.000000, 64.000000)'></polygon>
|
||||
<polygon points='480.000000,112.000000 468.000000,106.400002 468.000000,117.599998' fill='currentColor' transform='rotate(0.000000, 472.000000, 112.000000)'></polygon>
|
||||
<polygon points='480.000000,160.000000 468.000000,154.399994 468.000000,165.600006' fill='currentColor' transform='rotate(0.000000, 472.000000, 160.000000)'></polygon>
|
||||
<polygon points='672.000000,128.000000 660.000000,122.400002 660.000000,133.600006' fill='currentColor' transform='rotate(0.000000, 664.000000, 128.000000)'></polygon>
|
||||
<polygon points='672.000000,192.000000 660.000000,186.399994 660.000000,197.600006' fill='currentColor' transform='rotate(0.000000, 664.000000, 192.000000)'></polygon>
|
||||
<polygon points='816.000000,32.000000 804.000000,26.400000 804.000000,37.599998' fill='currentColor' transform='rotate(0.000000, 808.000000, 32.000000)'></polygon>
|
||||
<polygon points='816.000000,64.000000 804.000000,58.400002 804.000000,69.599998' fill='currentColor' transform='rotate(0.000000, 808.000000, 64.000000)'></polygon>
|
||||
<polygon points='816.000000,128.000000 804.000000,122.400002 804.000000,133.600006' fill='currentColor' transform='rotate(0.000000, 808.000000, 128.000000)'></polygon>
|
||||
<polygon points='816.000000,160.000000 804.000000,154.399994 804.000000,165.600006' fill='currentColor' transform='rotate(0.000000, 808.000000, 160.000000)'></polygon>
|
||||
<polygon points='816.000000,224.000000 804.000000,218.399994 804.000000,229.600006' fill='currentColor' transform='rotate(0.000000, 808.000000, 224.000000)'></polygon>
|
||||
<polygon points='816.000000,304.000000 804.000000,298.399994 804.000000,309.600006' fill='currentColor' transform='rotate(0.000000, 808.000000, 304.000000)'></polygon>
|
||||
<polygon points='816.000000,336.000000 804.000000,330.399994 804.000000,341.600006' fill='currentColor' transform='rotate(0.000000, 808.000000, 336.000000)'></polygon>
|
||||
<polygon points='816.000000,368.000000 804.000000,362.399994 804.000000,373.600006' fill='currentColor' transform='rotate(0.000000, 808.000000, 368.000000)'></polygon>
|
||||
<polygon points='816.000000,400.000000 804.000000,394.399994 804.000000,405.600006' fill='currentColor' transform='rotate(0.000000, 808.000000, 400.000000)'></polygon>
|
||||
<path d='M 496,16 A 16,16 0 0,0 480,32' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 608,16 A 16,16 0 0,1 624,32' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,48 A 16,16 0 0,0 160,64' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 304,48 A 16,16 0 0,1 320,64' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 160,64 A 16,16 0 0,0 176,80' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 320,64 A 16,16 0 0,1 304,80' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,96 A 16,16 0 0,0 160,112' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 320,96 A 16,16 0 0,1 336,112' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 688,96 A 16,16 0 0,0 672,112' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 752,96 A 16,16 0 0,1 768,112' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 160,128 A 16,16 0 0,0 176,144' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 336,128 A 16,16 0 0,1 320,144' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 360,144 A 16,16 0 0,0 344,160' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 416,144 A 16,16 0 0,1 432,160' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 480,160 A 16,16 0 0,0 496,176' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 624,160 A 16,16 0 0,1 608,176' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,208 A 16,16 0 0,0 160,224' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 280,208 A 16,16 0 0,1 296,224' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 672,192 A 16,16 0 0,0 688,208' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 768,192 A 16,16 0 0,1 752,208' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 160,224 A 16,16 0 0,0 176,240' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 296,224 A 16,16 0 0,1 280,240' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 344,224 A 16,16 0 0,0 360,240' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 432,224 A 16,16 0 0,1 416,240' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,320 A 16,16 0 0,0 160,336' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 352,320 A 16,16 0 0,1 368,336' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 160,336 A 16,16 0 0,0 176,352' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 368,336 A 16,16 0 0,1 352,352' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 176,384 A 16,16 0 0,0 160,400' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 264,384 A 16,16 0 0,1 280,400' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 160,400 A 16,16 0 0,0 176,416' fill='none' stroke='currentColor'></path>
|
||||
<path d='M 280,400 A 16,16 0 0,1 264,416' fill='none' stroke='currentColor'></path>
|
||||
<style>
|
||||
text {
|
||||
text-anchor: middle;
|
||||
font-family: "Menlo","Lucida Console","monospace";
|
||||
fill: currentColor;
|
||||
font-size: 1em;
|
||||
}
|
||||
</style>
|
||||
<text x='16' y='4'>N</text>
|
||||
<text x='24' y='4'>o</text>
|
||||
<text x='32' y='4'>r</text>
|
||||
<text x='40' y='4'>m</text>
|
||||
<text x='48' y='4'>a</text>
|
||||
<text x='56' y='4'>l</text>
|
||||
<text x='72' y='4'>f</text>
|
||||
<text x='80' y='4'>l</text>
|
||||
<text x='88' y='4'>o</text>
|
||||
<text x='96' y='4'>w</text>
|
||||
<text x='104' y='4'>:</text>
|
||||
<text x='32' y='36'>P</text>
|
||||
<text x='40' y='36'>D</text>
|
||||
<text x='48' y='36'>F</text>
|
||||
<text x='824' y='36'>P</text>
|
||||
<text x='832' y='36'>D</text>
|
||||
<text x='840' y='36'>F</text>
|
||||
<text x='856' y='36'>1</text>
|
||||
<text x='864' y='36'>.</text>
|
||||
<text x='872' y='36'>5</text>
|
||||
<text x='32' y='68'>P</text>
|
||||
<text x='40' y='68'>l</text>
|
||||
<text x='48' y='68'>a</text>
|
||||
<text x='56' y='68'>i</text>
|
||||
<text x='64' y='68'>n</text>
|
||||
<text x='72' y='68'>t</text>
|
||||
<text x='80' y='68'>e</text>
|
||||
<text x='88' y='68'>x</text>
|
||||
<text x='96' y='68'>t</text>
|
||||
<text x='192' y='68'>Q</text>
|
||||
<text x='200' y='68'>T</text>
|
||||
<text x='208' y='68'>e</text>
|
||||
<text x='216' y='68'>x</text>
|
||||
<text x='224' y='68'>t</text>
|
||||
<text x='232' y='68'>D</text>
|
||||
<text x='240' y='68'>o</text>
|
||||
<text x='248' y='68'>c</text>
|
||||
<text x='256' y='68'>u</text>
|
||||
<text x='264' y='68'>m</text>
|
||||
<text x='272' y='68'>e</text>
|
||||
<text x='280' y='68'>n</text>
|
||||
<text x='288' y='68'>t</text>
|
||||
<text x='504' y='68'>p</text>
|
||||
<text x='512' y='68'>d</text>
|
||||
<text x='520' y='68'>f</text>
|
||||
<text x='528' y='68'>2</text>
|
||||
<text x='536' y='68'>p</text>
|
||||
<text x='544' y='68'>r</text>
|
||||
<text x='552' y='68'>i</text>
|
||||
<text x='560' y='68'>n</text>
|
||||
<text x='568' y='68'>t</text>
|
||||
<text x='576' y='68'>a</text>
|
||||
<text x='584' y='68'>b</text>
|
||||
<text x='592' y='68'>l</text>
|
||||
<text x='600' y='68'>e</text>
|
||||
<text x='824' y='68'>P</text>
|
||||
<text x='832' y='68'>o</text>
|
||||
<text x='840' y='68'>s</text>
|
||||
<text x='848' y='68'>t</text>
|
||||
<text x='856' y='68'>s</text>
|
||||
<text x='864' y='68'>c</text>
|
||||
<text x='872' y='68'>r</text>
|
||||
<text x='880' y='68'>i</text>
|
||||
<text x='888' y='68'>p</text>
|
||||
<text x='896' y='68'>t</text>
|
||||
<text x='912' y='68'>2</text>
|
||||
<text x='32' y='100'>D</text>
|
||||
<text x='40' y='100'>O</text>
|
||||
<text x='48' y='100'>C</text>
|
||||
<text x='56' y='100'>/</text>
|
||||
<text x='64' y='100'>P</text>
|
||||
<text x='72' y='100'>P</text>
|
||||
<text x='80' y='100'>T</text>
|
||||
<text x='496' y='100'>(</text>
|
||||
<text x='504' y='100'>P</text>
|
||||
<text x='512' y='100'>o</text>
|
||||
<text x='520' y='100'>p</text>
|
||||
<text x='528' y='100'>p</text>
|
||||
<text x='536' y='100'>l</text>
|
||||
<text x='544' y='100'>e</text>
|
||||
<text x='552' y='100'>r</text>
|
||||
<text x='560' y='100'>+</text>
|
||||
<text x='568' y='100'>C</text>
|
||||
<text x='576' y='100'>a</text>
|
||||
<text x='584' y='100'>i</text>
|
||||
<text x='592' y='100'>r</text>
|
||||
<text x='600' y='100'>o</text>
|
||||
<text x='608' y='100'>)</text>
|
||||
<text x='32' y='116'>O</text>
|
||||
<text x='40' y='116'>D</text>
|
||||
<text x='48' y='116'>T</text>
|
||||
<text x='56' y='116'>/</text>
|
||||
<text x='64' y='116'>O</text>
|
||||
<text x='72' y='116'>D</text>
|
||||
<text x='80' y='116'>P</text>
|
||||
<text x='184' y='116'>C</text>
|
||||
<text x='192' y='116'>a</text>
|
||||
<text x='200' y='116'>l</text>
|
||||
<text x='208' y='116'>l</text>
|
||||
<text x='216' y='116'>i</text>
|
||||
<text x='224' y='116'>g</text>
|
||||
<text x='232' y='116'>r</text>
|
||||
<text x='240' y='116'>a</text>
|
||||
<text x='248' y='116'>c</text>
|
||||
<text x='256' y='116'>o</text>
|
||||
<text x='264' y='116'>n</text>
|
||||
<text x='272' y='116'>v</text>
|
||||
<text x='280' y='116'>e</text>
|
||||
<text x='288' y='116'>r</text>
|
||||
<text x='296' y='116'>t</text>
|
||||
<text x='304' y='116'>e</text>
|
||||
<text x='312' y='116'>r</text>
|
||||
<text x='208' y='132'>(</text>
|
||||
<text x='216' y='132'>o</text>
|
||||
<text x='224' y='132'>p</text>
|
||||
<text x='232' y='132'>t</text>
|
||||
<text x='240' y='132'>i</text>
|
||||
<text x='248' y='132'>o</text>
|
||||
<text x='256' y='132'>n</text>
|
||||
<text x='264' y='132'>a</text>
|
||||
<text x='272' y='132'>l</text>
|
||||
<text x='280' y='132'>)</text>
|
||||
<text x='824' y='132'>P</text>
|
||||
<text x='832' y='132'>W</text>
|
||||
<text x='840' y='132'>G</text>
|
||||
<text x='848' y='132'>-</text>
|
||||
<text x='856' y='132'>R</text>
|
||||
<text x='864' y='132'>a</text>
|
||||
<text x='872' y='132'>s</text>
|
||||
<text x='880' y='132'>t</text>
|
||||
<text x='888' y='132'>e</text>
|
||||
<text x='896' y='132'>r</text>
|
||||
<text x='696' y='148'>p</text>
|
||||
<text x='704' y='148'>p</text>
|
||||
<text x='712' y='148'>m</text>
|
||||
<text x='720' y='148'>2</text>
|
||||
<text x='728' y='148'>p</text>
|
||||
<text x='736' y='148'>w</text>
|
||||
<text x='744' y='148'>g</text>
|
||||
<text x='32' y='164'>I</text>
|
||||
<text x='40' y='164'>m</text>
|
||||
<text x='48' y='164'>a</text>
|
||||
<text x='56' y='164'>g</text>
|
||||
<text x='64' y='164'>e</text>
|
||||
<text x='72' y='164'>s</text>
|
||||
<text x='824' y='164'>U</text>
|
||||
<text x='832' y='164'>R</text>
|
||||
<text x='840' y='164'>F</text>
|
||||
<text x='848' y='164'>-</text>
|
||||
<text x='856' y='164'>R</text>
|
||||
<text x='864' y='164'>a</text>
|
||||
<text x='872' y='164'>s</text>
|
||||
<text x='880' y='164'>t</text>
|
||||
<text x='888' y='164'>e</text>
|
||||
<text x='896' y='164'>r</text>
|
||||
<text x='368' y='196'>Q</text>
|
||||
<text x='376' y='196'>I</text>
|
||||
<text x='384' y='196'>m</text>
|
||||
<text x='392' y='196'>a</text>
|
||||
<text x='400' y='196'>g</text>
|
||||
<text x='408' y='196'>e</text>
|
||||
<text x='32' y='228'>S</text>
|
||||
<text x='40' y='228'>V</text>
|
||||
<text x='48' y='228'>G</text>
|
||||
<text x='184' y='228'>Q</text>
|
||||
<text x='192' y='228'>S</text>
|
||||
<text x='200' y='228'>v</text>
|
||||
<text x='208' y='228'>g</text>
|
||||
<text x='216' y='228'>R</text>
|
||||
<text x='224' y='228'>e</text>
|
||||
<text x='232' y='228'>n</text>
|
||||
<text x='240' y='228'>d</text>
|
||||
<text x='248' y='228'>e</text>
|
||||
<text x='256' y='228'>r</text>
|
||||
<text x='264' y='228'>e</text>
|
||||
<text x='272' y='228'>r</text>
|
||||
<text x='824' y='228'>P</text>
|
||||
<text x='832' y='228'>N</text>
|
||||
<text x='840' y='228'>G</text>
|
||||
<text x='848' y='228'>,</text>
|
||||
<text x='864' y='228'>J</text>
|
||||
<text x='872' y='228'>P</text>
|
||||
<text x='880' y='228'>E</text>
|
||||
<text x='888' y='228'>G</text>
|
||||
<text x='896' y='228'>,</text>
|
||||
<text x='824' y='244'>R</text>
|
||||
<text x='832' y='244'>e</text>
|
||||
<text x='840' y='244'>v</text>
|
||||
<text x='848' y='244'>e</text>
|
||||
<text x='856' y='244'>r</text>
|
||||
<text x='864' y='244'>s</text>
|
||||
<text x='872' y='244'>e</text>
|
||||
<text x='888' y='244'>B</text>
|
||||
<text x='896' y='244'>M</text>
|
||||
<text x='904' y='244'>P</text>
|
||||
<text x='16' y='276'>I</text>
|
||||
<text x='24' y='276'>f</text>
|
||||
<text x='40' y='276'>t</text>
|
||||
<text x='48' y='276'>a</text>
|
||||
<text x='56' y='276'>r</text>
|
||||
<text x='64' y='276'>g</text>
|
||||
<text x='72' y='276'>e</text>
|
||||
<text x='80' y='276'>t</text>
|
||||
<text x='96' y='276'>f</text>
|
||||
<text x='104' y='276'>o</text>
|
||||
<text x='112' y='276'>r</text>
|
||||
<text x='120' y='276'>m</text>
|
||||
<text x='128' y='276'>a</text>
|
||||
<text x='136' y='276'>t</text>
|
||||
<text x='152' y='276'>i</text>
|
||||
<text x='160' y='276'>s</text>
|
||||
<text x='176' y='276'>t</text>
|
||||
<text x='184' y='276'>h</text>
|
||||
<text x='192' y='276'>e</text>
|
||||
<text x='208' y='276'>s</text>
|
||||
<text x='216' y='276'>a</text>
|
||||
<text x='224' y='276'>m</text>
|
||||
<text x='232' y='276'>e</text>
|
||||
<text x='240' y='276'>:</text>
|
||||
<text x='32' y='308'>P</text>
|
||||
<text x='40' y='308'>o</text>
|
||||
<text x='48' y='308'>s</text>
|
||||
<text x='56' y='308'>t</text>
|
||||
<text x='64' y='308'>s</text>
|
||||
<text x='72' y='308'>c</text>
|
||||
<text x='80' y='308'>r</text>
|
||||
<text x='88' y='308'>i</text>
|
||||
<text x='96' y='308'>p</text>
|
||||
<text x='104' y='308'>t</text>
|
||||
<text x='824' y='308'>P</text>
|
||||
<text x='832' y='308'>o</text>
|
||||
<text x='840' y='308'>s</text>
|
||||
<text x='848' y='308'>t</text>
|
||||
<text x='856' y='308'>s</text>
|
||||
<text x='864' y='308'>c</text>
|
||||
<text x='872' y='308'>r</text>
|
||||
<text x='880' y='308'>i</text>
|
||||
<text x='888' y='308'>p</text>
|
||||
<text x='896' y='308'>t</text>
|
||||
<text x='32' y='340'>P</text>
|
||||
<text x='40' y='340'>l</text>
|
||||
<text x='48' y='340'>a</text>
|
||||
<text x='56' y='340'>i</text>
|
||||
<text x='64' y='340'>n</text>
|
||||
<text x='72' y='340'>t</text>
|
||||
<text x='80' y='340'>e</text>
|
||||
<text x='88' y='340'>x</text>
|
||||
<text x='96' y='340'>t</text>
|
||||
<text x='184' y='340'>e</text>
|
||||
<text x='192' y='340'>n</text>
|
||||
<text x='200' y='340'>s</text>
|
||||
<text x='208' y='340'>u</text>
|
||||
<text x='216' y='340'>r</text>
|
||||
<text x='224' y='340'>e</text>
|
||||
<text x='240' y='340'>C</text>
|
||||
<text x='248' y='340'>R</text>
|
||||
<text x='256' y='340'>+</text>
|
||||
<text x='264' y='340'>L</text>
|
||||
<text x='272' y='340'>F</text>
|
||||
<text x='288' y='340'>n</text>
|
||||
<text x='296' y='340'>e</text>
|
||||
<text x='304' y='340'>w</text>
|
||||
<text x='312' y='340'>l</text>
|
||||
<text x='320' y='340'>i</text>
|
||||
<text x='328' y='340'>n</text>
|
||||
<text x='336' y='340'>e</text>
|
||||
<text x='344' y='340'>s</text>
|
||||
<text x='824' y='340'>P</text>
|
||||
<text x='832' y='340'>l</text>
|
||||
<text x='840' y='340'>a</text>
|
||||
<text x='848' y='340'>i</text>
|
||||
<text x='856' y='340'>n</text>
|
||||
<text x='864' y='340'>t</text>
|
||||
<text x='872' y='340'>e</text>
|
||||
<text x='880' y='340'>x</text>
|
||||
<text x='888' y='340'>t</text>
|
||||
<text x='32' y='372'>P</text>
|
||||
<text x='40' y='372'>N</text>
|
||||
<text x='48' y='372'>G</text>
|
||||
<text x='824' y='372'>P</text>
|
||||
<text x='832' y='372'>N</text>
|
||||
<text x='840' y='372'>G</text>
|
||||
<text x='32' y='404'>J</text>
|
||||
<text x='40' y='404'>P</text>
|
||||
<text x='48' y='404'>E</text>
|
||||
<text x='56' y='404'>G</text>
|
||||
<text x='184' y='404'>b</text>
|
||||
<text x='192' y='404'>a</text>
|
||||
<text x='200' y='404'>s</text>
|
||||
<text x='208' y='404'>e</text>
|
||||
<text x='216' y='404'>l</text>
|
||||
<text x='224' y='404'>i</text>
|
||||
<text x='232' y='404'>n</text>
|
||||
<text x='240' y='404'>i</text>
|
||||
<text x='248' y='404'>f</text>
|
||||
<text x='256' y='404'>y</text>
|
||||
<text x='824' y='404'>J</text>
|
||||
<text x='832' y='404'>P</text>
|
||||
<text x='840' y='404'>E</text>
|
||||
<text x='848' y='404'>G</text>
|
||||
<text x='824' y='420'>(</text>
|
||||
<text x='832' y='420'>B</text>
|
||||
<text x='840' y='420'>a</text>
|
||||
<text x='848' y='420'>s</text>
|
||||
<text x='856' y='420'>e</text>
|
||||
<text x='864' y='420'>l</text>
|
||||
<text x='872' y='420'>i</text>
|
||||
<text x='880' y='420'>n</text>
|
||||
<text x='888' y='420'>e</text>
|
||||
<text x='904' y='420'>p</text>
|
||||
<text x='912' y='420'>r</text>
|
||||
<text x='920' y='420'>o</text>
|
||||
<text x='928' y='420'>f</text>
|
||||
<text x='936' y='420'>i</text>
|
||||
<text x='944' y='420'>l</text>
|
||||
<text x='952' y='420'>e</text>
|
||||
<text x='960' y='420'>)</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 20 KiB |
|
@ -3,9 +3,10 @@ Type=Application
|
|||
Icon=harbour-seaprint
|
||||
X-Nemo-Application-Type=silica-qt5
|
||||
Exec=harbour-seaprint
|
||||
X-DBusActivatable=true
|
||||
Name=SeaPrint
|
||||
[X-Sailjail]
|
||||
Permissions=Internet;Pictures;Documents;Downloads;MediaIndexing;Compatibility
|
||||
Permissions=Internet;Pictures;Documents;Downloads;RemovableMedia;MediaIndexing;Compatibility
|
||||
OrganizationName=net.attah
|
||||
ApplicationName=seaprint
|
||||
|
||||
ExecDBus=harbour-seaprint --prestart
|
||||
|
|
|
@ -26,36 +26,36 @@ INSTALLS += i18n
|
|||
system(lrelease $$PWD/translations/*.ts)
|
||||
|
||||
CONFIG += sailfishapp
|
||||
QT += svg
|
||||
PKGCONFIG += mlite5 libcurl poppler glib-2.0 cairo
|
||||
QT += svg dbus
|
||||
PKGCONFIG += mlite5 libcurl glib-2.0
|
||||
INCLUDEPATH += $$system(pkg-config --cflags-only-I poppler-glib | sed 's/-I//g')
|
||||
LIBS += -lcurl -lglib-2.0 -lgobject-2.0 -ldl
|
||||
DEFINES += MADNESS=1
|
||||
|
||||
# Write version file
|
||||
VERSION_H = \
|
||||
"$${LITERAL_HASH}ifndef SEAPRINT_VERSION" \
|
||||
"$${LITERAL_HASH} define SEAPRINT_VERSION \"$$VERSION\"" \
|
||||
"$${LITERAL_HASH}endif"
|
||||
write_file($$$$OUT_PWD/seaprint_version.h, VERSION_H)
|
||||
DEFINES += PDF_CREATOR='\\"SeaPrint\ $$VERSION\\"'
|
||||
DEFINES += SEAPRINT_VERSION='\\"$$VERSION\\"'
|
||||
|
||||
SOURCES += src/harbour-seaprint.cpp \
|
||||
src/dbusadaptorbase.cpp \
|
||||
src/freedesktopdbusadaptor.cpp \
|
||||
src/rangelistchecker.cpp \
|
||||
src/convertchecker.cpp \
|
||||
src/convertworker.cpp \
|
||||
src/curlrequester.cpp \
|
||||
src/curlworker.cpp \
|
||||
src/imageitem.cpp \
|
||||
src/ippdiscovery.cpp \
|
||||
src/ippmsg.cpp \
|
||||
src/ippprinter.cpp \
|
||||
src/mimer.cpp \
|
||||
ppm2pwg/printparameters.cpp \
|
||||
ppm2pwg/ppm2pwg.cpp \
|
||||
ppm2pwg/pdf2printable.cpp \
|
||||
ppm2pwg/baselinify.cpp \
|
||||
ppm2pwg/bytestream/bytestream.cpp \
|
||||
src/overrider.cpp \
|
||||
src/settings.cpp \
|
||||
src/svgprovider.cpp
|
||||
src/printerworker.cpp \
|
||||
src/seaprintdbusadaptor.cpp \
|
||||
src/settings.cpp
|
||||
|
||||
DISTFILES += qml/harbour-seaprint.qml \
|
||||
qml/components/DependentOn.qml \
|
||||
qml/cover/CoverPage.qml \
|
||||
qml/components/*qml \
|
||||
qml/pages/*.qml \
|
||||
|
@ -86,27 +86,33 @@ TRANSLATIONS += translations/harbour-seaprint-de.ts \
|
|||
translations/harbour-seaprint-pl.ts
|
||||
|
||||
HEADERS += \
|
||||
src/dbusadaptorbase.h \
|
||||
src/freedesktopdbusadaptor.h \
|
||||
src/rangelistchecker.h \
|
||||
src/convertchecker.h \
|
||||
src/convertworker.h \
|
||||
src/curlrequester.h \
|
||||
src/curlworker.h \
|
||||
src/imageitem.h \
|
||||
src/ippdiscovery.h \
|
||||
src/ippmsg.h \
|
||||
src/ippprinter.h \
|
||||
src/mimer.h \
|
||||
ppm2pwg/ppm2pwg.h \
|
||||
ppm2pwg/pdf2printable.h \
|
||||
ppm2pwg/baselinify.h \
|
||||
ppm2pwg/madness.h \
|
||||
ppm2pwg/printparameters.h \
|
||||
ppm2pwg/PwgPgHdr.h \
|
||||
ppm2pwg/PwgPgHdr.codable \
|
||||
ppm2pwg/UrfPgHdr.h \
|
||||
ppm2pwg/UrfPgHdr.codable \
|
||||
ppm2pwg/bytestream/bytestream.h \
|
||||
ppm2pwg/bytestream/codable.h \
|
||||
ppm2pwg/argget.h \
|
||||
src/overrider.h \
|
||||
src/papersizes.h \
|
||||
src/settings.h \
|
||||
src/svgprovider.h
|
||||
src/printerworker.h \
|
||||
src/seaprintdbusadaptor.h \
|
||||
src/settings.h
|
||||
|
||||
INCLUDEPATH += ppm2pwg \
|
||||
ppm2pwg/bytestream
|
||||
|
|
2
ppm2pwg
2
ppm2pwg
|
@ -1 +1 @@
|
|||
Subproject commit fd04fc7128dc40a3ac813873820c5aa9e9be4d48
|
||||
Subproject commit acc32f3769c3b18bd6b94c55b882c7b7b778854c
|
|
@ -7,33 +7,34 @@ Setting {
|
|||
property var preferred_choices: []
|
||||
property string preferred_choice_suffix: ""
|
||||
|
||||
property var actual_choices: Utils.fixupChoices(name, choices, parent.selectedFileType)
|
||||
property var actual_choices: Utils.fixupChoices(name, choices, parent.selectedFileType, parent.printer)
|
||||
|
||||
property int num_large_choices: 8
|
||||
|
||||
displayValue: Utils.ippName(name, choice != undefined ? choice : default_choice)
|
||||
displayValue: Utils.ippName(name, choice != undefined ? choice : default_choice, strings)
|
||||
|
||||
onClicked: {
|
||||
if(actual_choices.length>num_large_choices)
|
||||
if(hasMenu)
|
||||
{
|
||||
menu.open(this)
|
||||
}
|
||||
else if(actual_choices.length != 0)
|
||||
{
|
||||
var dialog = pageStack.push("LargeChoiceDialog.qml",
|
||||
{name:name, choice: choice != undefined ? choice : default_choice, choices: actual_choices,
|
||||
preferred_choices: preferred_choices, preferred_choice_suffix: preferred_choice_suffix})
|
||||
preferred_choices: preferred_choices, preferred_choice_suffix: preferred_choice_suffix,
|
||||
strings: strings})
|
||||
dialog.accepted.connect(function() {
|
||||
choice = dialog.choice
|
||||
})
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.open(this)
|
||||
}
|
||||
}
|
||||
|
||||
menu: ContextMenu {
|
||||
Repeater {
|
||||
model: actual_choices.length>num_large_choices ? 0 : actual_choices
|
||||
MenuItem {
|
||||
text: Utils.ippName(name, actual_choices[index])
|
||||
text: Utils.ippName(name, actual_choices[index], strings)
|
||||
+ (Utils.has(preferred_choices, actual_choices[index]) ? " "+preferred_choice_suffix : "")
|
||||
onClicked:
|
||||
{
|
||||
|
@ -44,7 +45,5 @@ Setting {
|
|||
|
||||
}
|
||||
|
||||
hasMenu: !actual_choices.length>num_large_choices
|
||||
|
||||
|
||||
hasMenu: actual_choices.length != 0 && (actual_choices.length <= num_large_choices)
|
||||
}
|
||||
|
|
|
@ -71,4 +71,34 @@ GalleryFilterUnion {
|
|||
property: "fileName"
|
||||
value: ".RTF"
|
||||
}
|
||||
|
||||
GalleryEndsWithFilter {
|
||||
property: "fileName"
|
||||
value: ".ppt"
|
||||
}
|
||||
|
||||
GalleryEndsWithFilter {
|
||||
property: "fileName"
|
||||
value: ".pptx"
|
||||
}
|
||||
|
||||
GalleryEndsWithFilter {
|
||||
property: "fileName"
|
||||
value: ".odp"
|
||||
}
|
||||
|
||||
GalleryEndsWithFilter {
|
||||
property: "fileName"
|
||||
value: ".PPT"
|
||||
}
|
||||
|
||||
GalleryEndsWithFilter {
|
||||
property: "fileName"
|
||||
value: ".PPTX"
|
||||
}
|
||||
|
||||
GalleryEndsWithFilter {
|
||||
property: "fileName"
|
||||
value: ".ODP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,6 @@ Dialog {
|
|||
property string title
|
||||
canAccept: valueField.acceptableInput
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
|
||||
console.log("adasdsa", title)
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
|
||||
|
@ -26,10 +20,9 @@ Dialog {
|
|||
id: valueField
|
||||
validator: IntValidator{bottom: min; top: max;}
|
||||
width: parent.width
|
||||
placeholderText: ""+min+"-"+max
|
||||
placeholderText: ""+min+"-"+(max == 65535 ? "..." : max)
|
||||
label: title
|
||||
focus: true
|
||||
labelVisible: true
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
|
||||
}
|
||||
|
|
|
@ -2,8 +2,21 @@ import QtQuick 2.6
|
|||
import Sailfish.Silica 1.0
|
||||
|
||||
Setting {
|
||||
property int low: valid ? parent.printer.attrs[name+"-supported"].value.low : 0
|
||||
property int high: valid ? parent.printer.attrs[name+"-supported"].value.high : 0
|
||||
property int minimum_high: 0
|
||||
property int low: _valid ? parent.printer.attrs[name+"-supported"].value.low : (minimum_high != 0) ? 1 : 0
|
||||
property int high: _valid ? ensure_minimum(parent.printer.attrs[name+"-supported"].value.high) : minimum_high
|
||||
|
||||
function ensure_minimum(orig)
|
||||
{
|
||||
if(orig < minimum_high)
|
||||
{
|
||||
return minimum_high;
|
||||
}
|
||||
else
|
||||
{
|
||||
return orig;
|
||||
}
|
||||
}
|
||||
|
||||
property bool suppressChange: false
|
||||
|
||||
|
@ -12,7 +25,6 @@ Setting {
|
|||
onChoiceChanged: {
|
||||
if(choice == undefined)
|
||||
{
|
||||
console.log("choice unset");
|
||||
suppressChange = true;
|
||||
slider.value = slider.minimumValue;
|
||||
suppressChange = false;
|
||||
|
@ -41,7 +53,7 @@ Setting {
|
|||
anchors.right: parent.right
|
||||
icon.source: "image://theme/icon-s-edit"
|
||||
onClicked: {var dialog = pageStack.push(Qt.resolvedUrl("IntegerInputDialog.qml"),
|
||||
{value: choice, title: prettyName,
|
||||
{title: prettyName,
|
||||
min: low, max: high});
|
||||
dialog.accepted.connect(function() {
|
||||
choice = dialog.value;
|
||||
|
|
|
@ -12,6 +12,7 @@ Dialog {
|
|||
property var choices
|
||||
property var preferred_choices
|
||||
property string preferred_choice_suffix
|
||||
property var strings
|
||||
canAccept: false
|
||||
|
||||
SilicaListView
|
||||
|
@ -30,7 +31,7 @@ Dialog {
|
|||
x: Theme.paddingLarge
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
highlighted: choices[index]==new_choice
|
||||
text: Utils.ippName(name, choices[index])
|
||||
text: Utils.ippName(name, choices[index], strings)
|
||||
+ (Utils.has(preferred_choices, choices[index]) ? " "+preferred_choice_suffix : "")
|
||||
}
|
||||
}
|
||||
|
|
30
qml/components/RangeListInputDialog.qml
Normal file
30
qml/components/RangeListInputDialog.qml
Normal file
|
@ -0,0 +1,30 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import seaprint.rangelistchecker 1.0
|
||||
|
||||
Dialog {
|
||||
id: dialog
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string title
|
||||
property var value
|
||||
canAccept: valueField.acceptableInput
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
|
||||
DialogHeader { }
|
||||
|
||||
TextField {
|
||||
id: valueField
|
||||
acceptableInput: Object.keys(value).length !== 0
|
||||
width: parent.width
|
||||
placeholderText: "1,2,17-42"
|
||||
label: title
|
||||
focus: true
|
||||
labelVisible: true
|
||||
onTextChanged: { dialog.value = RangeListChecker.parse(text) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ Setting {
|
|||
property int high
|
||||
property int choice_low: 1
|
||||
property int choice_high: 0
|
||||
property bool acceptRangeList: false
|
||||
|
||||
property bool suppressChange: false
|
||||
|
||||
|
@ -41,9 +42,8 @@ Setting {
|
|||
}
|
||||
|
||||
onChoiceChanged: {
|
||||
if(choice == undefined)
|
||||
if(choice == undefined || choice.constructor.name !== "Object")
|
||||
{
|
||||
console.log("choice unset");
|
||||
suppressChange = true;
|
||||
low_slider.value = low_slider.minimumValue;
|
||||
high_slider.value = high_slider.minimumValue;
|
||||
|
@ -51,7 +51,39 @@ Setting {
|
|||
}
|
||||
}
|
||||
|
||||
displayValue: choice == undefined ? qsTr("all") : ""+choice.low+" - "+choice.high
|
||||
displayValue: prettify(choice)
|
||||
|
||||
function prettify(choice)
|
||||
{
|
||||
if(choice == undefined)
|
||||
{
|
||||
return qsTr("all");
|
||||
}
|
||||
else if(choice.constructor.name === "Object")
|
||||
{
|
||||
return (""+choice.low+" - "+choice.high)
|
||||
}
|
||||
else
|
||||
{
|
||||
var ret = "";
|
||||
for(var i = 0; i < choice.length; i++)
|
||||
{
|
||||
if(i!=0)
|
||||
{
|
||||
ret = ret+","
|
||||
}
|
||||
if(choice[i].low == choice[i].high)
|
||||
{
|
||||
ret=ret+choice[i].low
|
||||
}
|
||||
else
|
||||
{
|
||||
ret=ret+choice[i].low+"-"+choice[i].high
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
|
@ -72,7 +104,7 @@ Setting {
|
|||
anchors.right: parent.right
|
||||
icon.source: "image://theme/icon-s-edit"
|
||||
onClicked: {var dialog = pageStack.push(Qt.resolvedUrl("IntegerInputDialog.qml"),
|
||||
{value: choice, title: prettyName,
|
||||
{title: prettyName + " (" + qsTr("Low") + ")",
|
||||
min: 1, max: high});
|
||||
dialog.accepted.connect(function() {
|
||||
choice_low = dialog.value;
|
||||
|
@ -100,7 +132,7 @@ Setting {
|
|||
anchors.right: parent.right
|
||||
icon.source: "image://theme/icon-s-edit"
|
||||
onClicked: {var dialog = pageStack.push(Qt.resolvedUrl("IntegerInputDialog.qml"),
|
||||
{value: choice, title: prettyName,
|
||||
{title: prettyName + " (" + qsTr("High") + ")",
|
||||
min: 1, max: high});
|
||||
dialog.accepted.connect(function() {
|
||||
choice_high = dialog.value;
|
||||
|
@ -110,6 +142,17 @@ Setting {
|
|||
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
visible: acceptRangeList
|
||||
text: qsTr("Advanced")
|
||||
onClicked: {var dialog = pageStack.push(Qt.resolvedUrl("RangeListInputDialog.qml"),
|
||||
{title: prettyName});
|
||||
dialog.accepted.connect(function() {
|
||||
choice = dialog.value;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ Item {
|
|||
property var choice
|
||||
property var default_choice: parent.getDefaultChoice(name)
|
||||
|
||||
property var strings: parent.printer.strings
|
||||
|
||||
Component.onCompleted: parent.setInitialChoice(this)
|
||||
|
||||
onChoiceChanged: parent.choiceMade(this)
|
||||
|
|
|
@ -84,6 +84,6 @@ Column {
|
|||
delete jobParams[setting.subkey];
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify(jobParams));
|
||||
// console.log(JSON.stringify(jobParams));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,10 @@ import "../components"
|
|||
property var color: "white"
|
||||
property int level: 100
|
||||
property int high_level: 100
|
||||
property int low_level: 0
|
||||
property int low_level: 10
|
||||
property string name: ""
|
||||
property string type: ""
|
||||
property real value_fraction: level/(high_level == 0 ? 100 : high_level)
|
||||
|
||||
spacing: Theme.paddingMedium
|
||||
bottomPadding: Theme.paddingMedium
|
||||
|
@ -19,14 +20,14 @@ import "../components"
|
|||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: parent.color
|
||||
value: (level/high_level)
|
||||
value: value_fraction
|
||||
}
|
||||
Label
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Theme.itemSizeExtraSmall
|
||||
text: ""+(100*level/high_level)+"%"
|
||||
color: Theme.highlightColor
|
||||
text: ""+Math.round(100*value_fraction)+"%"
|
||||
color: level <= low_level ? "red" : Theme.highlightColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
}
|
||||
Column
|
||||
|
|
|
@ -20,12 +20,10 @@ Item {
|
|||
Component.onCompleted: go()
|
||||
|
||||
function servicesChanged() {
|
||||
console.log("services changed");
|
||||
go();
|
||||
}
|
||||
|
||||
function go() {
|
||||
console.log("go!")
|
||||
call("GetServices", undefined,
|
||||
function(result) {
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
|
|
|
@ -13,9 +13,37 @@ ApplicationWindow
|
|||
{
|
||||
id: appWin
|
||||
|
||||
property Page _mainPage
|
||||
|
||||
property bool expectCalligra: true
|
||||
|
||||
initialPage: Component { FirstPage { selectedFile: Qt.application.arguments[1] ? Qt.application.arguments[1] : "" } }
|
||||
|
||||
function openFile(file) {
|
||||
selectedFile = file
|
||||
selectedFileType = Mimer.get_type(selectedFile);
|
||||
if(selectedFileType == "")
|
||||
{
|
||||
selectedFile = ""
|
||||
notifier.notify(qsTr("Unsupported document format"))
|
||||
}
|
||||
if(pageStack.currentPage.busyPage)
|
||||
{
|
||||
notifier.notify(qsTr("New file selected"))
|
||||
}
|
||||
else
|
||||
{
|
||||
pageStack.pop(appWin._mainPage, PageStackAction.Immediate)
|
||||
}
|
||||
}
|
||||
|
||||
property string selectedFile: ""
|
||||
property string selectedFileType
|
||||
|
||||
initialPage: Component {
|
||||
FirstPage {
|
||||
Component.onCompleted: appWin._mainPage = this
|
||||
}
|
||||
}
|
||||
cover: Qt.resolvedUrl("cover/CoverPage.qml")
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
|
@ -51,11 +79,9 @@ ApplicationWindow
|
|||
db_conn.transaction(function (tx) {
|
||||
var res = tx.executeSql('SELECT * FROM Favourites WHERE ssid=?', [ssid]);
|
||||
for (var i = 0; i < res.rows.length; i++) {
|
||||
console.log(res.rows.item(i).url)
|
||||
favs.push(res.rows.item(i).url);
|
||||
}
|
||||
});
|
||||
console.log(ssid, favs);
|
||||
return favs
|
||||
}
|
||||
|
||||
|
@ -77,10 +103,18 @@ ApplicationWindow
|
|||
}
|
||||
|
||||
function simplifyType(mimetype) {
|
||||
if(Mimer.isImage(mimetype))
|
||||
if(mimetype == Mimer.SVG)
|
||||
{
|
||||
return {simple: "svg", translatable: qsTr("SVGs")};
|
||||
}
|
||||
else if(Mimer.isImage(mimetype))
|
||||
{
|
||||
return {simple: "image", translatable: qsTr("images")};
|
||||
}
|
||||
else if(mimetype == Mimer.Plaintext)
|
||||
{
|
||||
return {simple: "plaintext", translatable: qsTr("plaintext")};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {simple: "document", translatable: qsTr("documents")};
|
||||
|
@ -123,7 +157,6 @@ ApplicationWindow
|
|||
expireTimeout: 4000
|
||||
|
||||
function notify(data) {
|
||||
console.log("notifyMessage", data)
|
||||
body = data
|
||||
previewBody = data
|
||||
publish()
|
||||
|
@ -152,5 +185,19 @@ ApplicationWindow
|
|||
defaultValue: SeaPrintSettings.ignoreSslErrorsDefault
|
||||
}
|
||||
|
||||
ConfigurationValue
|
||||
{
|
||||
id: debugLogSetting
|
||||
key: SeaPrintSettings.debugLogPath
|
||||
defaultValue: SeaPrintSettings.debugLogDefault
|
||||
}
|
||||
|
||||
ConfigurationValue
|
||||
{
|
||||
id: allowExternalConnectionsSetting
|
||||
key: SeaPrintSettings.allowExternalConnectionsPath
|
||||
defaultValue: SeaPrintSettings.allowExternalConnectionsDefault
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ Page {
|
|||
AboutLabel {
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primaryColor
|
||||
text: "© 2019-2021 Anton Thomasson"
|
||||
text: "© 2019-2023 Anton Thomasson et al."
|
||||
}
|
||||
|
||||
|
||||
|
@ -99,10 +99,10 @@ Page {
|
|||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: qsTr("Chinese")+" - dashinfantry\n"+
|
||||
qsTr("Dutch")+" - JSEHV, Rudi Timmermans\n"+
|
||||
qsTr("French")+" - ensag-dev, Quentí\n"+
|
||||
qsTr("German")+" - black-sheep-dev, wheelseal, cnlpete\n"+
|
||||
qsTr("Polish")+" - atlochowski\n"+
|
||||
qsTr("Spanish")+" - carmenfdezb"
|
||||
qsTr("French")+" - ensag-dev, pherjung, phklrz, Quentí\n"+
|
||||
qsTr("German")+" - black-sheep-dev, cnlpete, wheelseal\n"+
|
||||
qsTr("Polish")+" - atlochowski, karolkurek\n"+
|
||||
qsTr("Spanish")+" - carmenfdezb, JSEHV"
|
||||
}
|
||||
|
||||
SectionHeader { text: qsTr("Optional dependencies") }
|
||||
|
|
|
@ -3,6 +3,8 @@ import Sailfish.Silica 1.0
|
|||
import "utils.js" as Utils
|
||||
|
||||
Page {
|
||||
property bool busyPage: true
|
||||
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
Component.onCompleted: {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import "utils.js" as Utils
|
||||
|
||||
Page {
|
||||
|
||||
|
@ -17,7 +18,7 @@ Page {
|
|||
|
||||
PageHeader {
|
||||
id: header
|
||||
title: printer.attrs["printer-name"].value
|
||||
title: Utils.unknownForEmptyString(printer.attrs["printer-name"].value)
|
||||
}
|
||||
|
||||
TextArea {
|
||||
|
|
|
@ -12,6 +12,18 @@ Page {
|
|||
anchors.fill: parent
|
||||
contentHeight: col.height
|
||||
|
||||
PullDownMenu {
|
||||
visible: identifyLabel.visible
|
||||
|
||||
MenuItem {
|
||||
id: identifyLabel
|
||||
text: qsTr("Identify printer")
|
||||
visible: printer.attrs.hasOwnProperty("identify-actions-supported")
|
||||
onClicked: printer.identify()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column {
|
||||
id: col
|
||||
|
||||
|
@ -19,7 +31,7 @@ Page {
|
|||
width: Math.min(Screen.width, detailsFlickable.width)-2*Theme.paddingLarge
|
||||
PageHeader {
|
||||
title: qsTr("Details")
|
||||
description: printer.attrs["printer-name"].value
|
||||
description: Utils.unknownForEmptyString(printer.attrs["printer-name"].value)
|
||||
}
|
||||
|
||||
// TODO:
|
||||
|
@ -69,6 +81,27 @@ Page {
|
|||
visible: printer.attrs.hasOwnProperty("printer-state-message")
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Supply status")
|
||||
visible: printer.attrs.hasOwnProperty("marker-colors")
|
||||
}
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: printer.attrs["marker-colors"].value.length
|
||||
|
||||
SupplyItem
|
||||
{
|
||||
color: printer.attrs["marker-colors"].value[index]
|
||||
level: printer.attrs["marker-levels"].value[index]
|
||||
high_level: printer.attrs["marker-high-levels"].value[index]
|
||||
low_level: printer.attrs["marker-low-levels"].value[index]
|
||||
name: printer.attrs["marker-names"].value[index]
|
||||
type: printer.attrs["marker-types"].value[index]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("IPP versions")
|
||||
visible: printer.attrs.hasOwnProperty("ipp-versions-supported")
|
||||
|
@ -115,25 +148,24 @@ Page {
|
|||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Supply status")
|
||||
visible: printer.attrs.hasOwnProperty("marker-colors")
|
||||
text: qsTr("Performance")
|
||||
visible: printer.attrs.hasOwnProperty("pages-per-minute") || printer.attrs.hasOwnProperty("pages-per-minute-color")
|
||||
}
|
||||
|
||||
Repeater
|
||||
Label
|
||||
{
|
||||
model: printer.attrs["marker-colors"].value.length
|
||||
text: ""+printer.attrs["pages-per-minute"].value+" "+qsTr("pages/min")
|
||||
visible: printer.attrs.hasOwnProperty("pages-per-minute")
|
||||
|
||||
SupplyItem
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
color: printer.attrs["marker-colors"].value[index]
|
||||
level: printer.attrs["marker-levels"].value[index]
|
||||
high_level: printer.attrs["marker-high-levels"].value[index]
|
||||
low_level: printer.attrs["marker-low-levels"].value[index]
|
||||
name: printer.attrs["marker-names"].value[index]
|
||||
type: printer.attrs["marker-types"].value[index]
|
||||
text: ""+printer.attrs["pages-per-minute-color"].value+" "+qsTr("pages/min (color)")
|
||||
visible: printer.attrs.hasOwnProperty("pages-per-minute-color")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import Sailfish.Pickers 1.0
|
|||
import seaprint.ippdiscovery 1.0
|
||||
import seaprint.convertchecker 1.0
|
||||
import seaprint.ippprinter 1.0
|
||||
import seaprint.imageitem 1.0
|
||||
import seaprint.mimer 1.0
|
||||
import "utils.js" as Utils
|
||||
|
||||
|
@ -11,16 +12,11 @@ Page {
|
|||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string selectedFile: ""
|
||||
property string selectedFileType
|
||||
|
||||
Connections {
|
||||
target: wifi
|
||||
onConnectedChanged: {
|
||||
console.log("conn", wifi.connected, wifi.ssid)
|
||||
if(wifi.connected) {
|
||||
var favourites = db.getFavourites(wifi.ssid);
|
||||
console.log(favourites);
|
||||
IppDiscovery.favourites = favourites;
|
||||
}
|
||||
else {
|
||||
|
@ -31,7 +27,6 @@ Page {
|
|||
property bool initialSSIDchange: true
|
||||
|
||||
onSsidChanged: {
|
||||
console.log("ssid changed", wifi.ssid);
|
||||
if(!initialSSIDchange)
|
||||
{
|
||||
IppDiscovery.reset();
|
||||
|
@ -62,12 +57,6 @@ Page {
|
|||
|
||||
Component.onCompleted: {
|
||||
IppDiscovery.discover();
|
||||
if(selectedFile != "")
|
||||
{
|
||||
var type = Mimer.get_type(selectedFile);
|
||||
console.log(type);
|
||||
selectedFileType = type;
|
||||
}
|
||||
}
|
||||
|
||||
property bool nagged: false
|
||||
|
@ -75,7 +64,6 @@ Page {
|
|||
onStatusChanged: {
|
||||
if(status==PageStatus.Active && !nagged && nagScreenSetting.value != nagScreenSetting.expectedValue)
|
||||
{
|
||||
console.log("Can convert from Office:", ConvertChecker.calligra)
|
||||
if(expectCalligra && !ConvertChecker.calligra)
|
||||
{
|
||||
nagged=true
|
||||
|
@ -107,7 +95,6 @@ Page {
|
|||
onClicked: {
|
||||
var dialog = pageStack.push(Qt.resolvedUrl("AddPrinterDialog.qml"));
|
||||
dialog.accepted.connect(function() {
|
||||
console.log("add", wifi.ssid, dialog.value);
|
||||
db.addFavourite(wifi.ssid, dialog.value);
|
||||
IppDiscovery.favourites = db.getFavourites(dialog.ssid);
|
||||
})
|
||||
|
@ -146,9 +133,9 @@ Page {
|
|||
|
||||
visible: Object.keys(printer.attrs).length !== 0
|
||||
|
||||
property string name: printer.attrs["printer-name"].value != "" ? printer.attrs["printer-name"].value : qsTr("Unknown")
|
||||
property string name: Utils.unknownForEmptyString(printer.attrs["printer-name"].value)
|
||||
property var supported_formats: Utils.supported_formats(printer, considerAdditionalFormatsSetting.value)
|
||||
property bool canPrint: supported_formats.mimetypes.indexOf(selectedFileType) != -1
|
||||
property bool canPrint: supported_formats.mimetypes.indexOf(appWin.selectedFileType) != -1
|
||||
|
||||
Connections {
|
||||
target: printer
|
||||
|
@ -166,7 +153,6 @@ Page {
|
|||
Connections {
|
||||
target: page
|
||||
onRefreshed: {
|
||||
console.log("onRefreshed")
|
||||
printer.refresh()
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +174,7 @@ Page {
|
|||
{
|
||||
if(printer.attrs.hasOwnProperty("printer-uuid"))
|
||||
{
|
||||
return JSON.parse(db.getJobSettings(printer.attrs["printer-uuid"].value, selectedFileType));
|
||||
return JSON.parse(db.getJobSettings(printer.attrs["printer-uuid"].value, appWin.selectedFileType));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -205,7 +191,7 @@ Page {
|
|||
return;
|
||||
}
|
||||
debugCountReset.restart();
|
||||
if(selectedFile == "")
|
||||
if(appWin.selectedFile == "")
|
||||
{
|
||||
noFileSelected();
|
||||
}
|
||||
|
@ -215,11 +201,11 @@ Page {
|
|||
}
|
||||
else
|
||||
{
|
||||
pageStack.push(Qt.resolvedUrl("PrinterPage.qml"), {printer: printer, selectedFile: selectedFile, jobParams: maybeGetParams()})
|
||||
pageStack.push(Qt.resolvedUrl("PrinterPage.qml"), {printer: printer, selectedFile: appWin.selectedFile, jobParams: maybeGetParams()})
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
ImageItem {
|
||||
id: icon
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
|
@ -228,13 +214,17 @@ Page {
|
|||
|
||||
height: Theme.itemSizeLarge
|
||||
width: Theme.itemSizeLarge
|
||||
image: printer.icon
|
||||
|
||||
Image {
|
||||
id: placeholder
|
||||
anchors.fill: parent
|
||||
sourceSize.height: height
|
||||
sourceSize.width: width
|
||||
source: printer.attrs["printer-icons"] ? "image://ippdiscovery/"+Utils.selectIcon(printer.attrs["printer-icons"].value)
|
||||
: "image://svg/qml/pages/icon-seaprint-nobg.svg"
|
||||
// Some printers serve their icons over https with invalid certs...
|
||||
onStatusChanged: if (status == Image.Error) source = "image://svg/qml/pages/icon-seaprint-nobg.svg"
|
||||
|
||||
visible: !parent.valid
|
||||
source: "icon-seaprint-nobg.svg"
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
|
@ -252,13 +242,13 @@ Page {
|
|||
|
||||
Label {
|
||||
id: name_label
|
||||
color: canPrint || selectedFile == "" ? Theme.primaryColor : Theme.secondaryColor
|
||||
color: canPrint || appWin.selectedFile == "" ? Theme.primaryColor : Theme.secondaryColor
|
||||
text: name
|
||||
}
|
||||
|
||||
Label {
|
||||
id: mm_label
|
||||
color: canPrint || selectedFile == "" ? Theme.primaryColor : Theme.secondaryColor
|
||||
color: canPrint || appWin.selectedFile == "" ? Theme.primaryColor : Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
text: Utils.unknownForEmptyString(printer.attrs["printer-make-and-model"].value)
|
||||
+ (Utils.existsAndNotEmpty("printer-location", printer) ? " • "+printer.attrs["printer-location"].value : "")
|
||||
|
@ -266,7 +256,7 @@ Page {
|
|||
|
||||
Label {
|
||||
id: uri_label
|
||||
color: canPrint || selectedFile == "" ? Theme.highlightColor : Theme.secondaryColor
|
||||
color: canPrint || appWin.selectedFile == "" ? Theme.highlightColor : Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeTiny
|
||||
text: printer.url
|
||||
}
|
||||
|
@ -287,7 +277,7 @@ Page {
|
|||
width: Theme.itemSizeExtraSmall/2
|
||||
visible: supported_formats.pdf
|
||||
highlightColor: "red"
|
||||
highlighted: !(selectedFile == "" || canPrint)
|
||||
highlighted: !(appWin.selectedFile == "" || canPrint)
|
||||
source: "image://theme/icon-m-file-pdf"
|
||||
}
|
||||
HighlightImage {
|
||||
|
@ -295,7 +285,7 @@ Page {
|
|||
width: Theme.itemSizeExtraSmall/2
|
||||
visible: supported_formats.postscript
|
||||
highlightColor: "red"
|
||||
highlighted: !(selectedFile == "" || canPrint)
|
||||
highlighted: !(appWin.selectedFile == "" || canPrint)
|
||||
source: "image://theme/icon-m-file-other"
|
||||
|
||||
}
|
||||
|
@ -304,7 +294,7 @@ Page {
|
|||
width: Theme.itemSizeExtraSmall/2
|
||||
visible: supported_formats.plaintext
|
||||
highlightColor: "red"
|
||||
highlighted: !(selectedFile == "" || canPrint)
|
||||
highlighted: !(appWin.selectedFile == "" || canPrint)
|
||||
source: "image://theme/icon-m-file-document"
|
||||
}
|
||||
HighlightImage {
|
||||
|
@ -312,15 +302,23 @@ Page {
|
|||
width: Theme.itemSizeExtraSmall/2
|
||||
visible: supported_formats.office
|
||||
highlightColor: "red"
|
||||
highlighted: !(selectedFile == "" || canPrint)
|
||||
highlighted: !(appWin.selectedFile == "" || canPrint)
|
||||
source: "image://theme/icon-m-file-formatted"
|
||||
}
|
||||
HighlightImage {
|
||||
height: Theme.itemSizeExtraSmall/2
|
||||
width: Theme.itemSizeExtraSmall/2
|
||||
visible: supported_formats.office
|
||||
highlightColor: "red"
|
||||
highlighted: !(appWin.selectedFile == "" || canPrint)
|
||||
source: "image://theme/icon-m-file-presentation"
|
||||
}
|
||||
HighlightImage {
|
||||
height: Theme.itemSizeExtraSmall/2
|
||||
width: Theme.itemSizeExtraSmall/2
|
||||
visible: supported_formats.images
|
||||
highlightColor: "red"
|
||||
highlighted: !(selectedFile == "" || canPrint)
|
||||
highlighted: !(appWin.selectedFile == "" || canPrint)
|
||||
source: "image://theme/icon-m-file-image"
|
||||
}
|
||||
}
|
||||
|
@ -354,12 +352,16 @@ Page {
|
|||
}
|
||||
}
|
||||
}
|
||||
DockedPanel {
|
||||
Rectangle {
|
||||
id: fileDock
|
||||
open: true
|
||||
height: panelColumn.implicitHeight
|
||||
width: parent.width
|
||||
dock: Dock.Bottom
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: Theme.rgba(Theme.highlightBackgroundColor, 0.15) }
|
||||
GradientStop { position: 1.0; color: Theme.rgba(Theme.highlightBackgroundColor, 0.3) }
|
||||
}
|
||||
|
||||
Column {
|
||||
id: panelColumn
|
||||
|
@ -371,7 +373,7 @@ Page {
|
|||
Row {
|
||||
id: warningRow
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: Mimer.isOffice(page.selectedFileType)
|
||||
visible: Mimer.isOffice(appWin.selectedFileType)
|
||||
|
||||
HighlightImage {
|
||||
source: "image://theme/icon-s-warning"
|
||||
|
@ -394,7 +396,7 @@ Page {
|
|||
|
||||
horizontalAlignment: contentWidth > width ? Text.AlignRight : Text.AlignHCenter
|
||||
truncationMode: TruncationMode.Fade
|
||||
text: selectedFile != "" ? selectedFile : qsTr("No file selected")
|
||||
text: appWin.selectedFile != "" ? appWin.selectedFile : qsTr("No file selected")
|
||||
|
||||
SequentialAnimation {
|
||||
id: noFileSelectedAnimation
|
||||
|
@ -407,7 +409,6 @@ Page {
|
|||
Connections {
|
||||
target: page
|
||||
onNoFileSelected: {
|
||||
console.log("onNoFileSelected")
|
||||
noFileSelectedAnimation.start()
|
||||
}
|
||||
}
|
||||
|
@ -448,19 +449,7 @@ Page {
|
|||
title: qsTr("Choose file")
|
||||
|
||||
onSelectedContentPropertiesChanged: {
|
||||
var mimeType = Mimer.get_type(selectedContentProperties.filePath)
|
||||
if(mimeType == Mimer.PDF || mimeType == Mimer.Postscript || mimeType == Mimer.Plaintext || Mimer.isOffice(mimeType))
|
||||
{
|
||||
page.selectedFile = selectedContentProperties.filePath
|
||||
page.selectedFileType = mimeType
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log("UNSUPPORTED", mimeType);
|
||||
notifier.notify(qsTr("Unsupported document format"))
|
||||
page.selectedFile = ""
|
||||
page.selectedFileType = ""
|
||||
}
|
||||
appWin.openFile(selectedContentProperties.filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -470,8 +459,7 @@ Page {
|
|||
allowedOrientations: Orientation.All
|
||||
|
||||
onSelectedContentPropertiesChanged: {
|
||||
page.selectedFile = selectedContentProperties.filePath
|
||||
page.selectedFileType = Mimer.get_type(selectedContentProperties.filePath)
|
||||
appWin.openFile(selectedContentProperties.filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ Page {
|
|||
|
||||
Component.onCompleted: {
|
||||
printer.getJobs()
|
||||
console.log(JSON.stringify(printer.jobs))
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -37,7 +36,6 @@ Page {
|
|||
MenuItem {
|
||||
text: qsTr("Refresh")
|
||||
onClicked: {
|
||||
console.log(JSON.stringify(printer.jobs))
|
||||
printer.getJobs();
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +49,7 @@ Page {
|
|||
|
||||
header: PageHeader {
|
||||
id: pageHeader
|
||||
title: printer.attrs["printer-name"].value
|
||||
title: Utils.unknownForEmptyString(printer.attrs["printer-name"].value)
|
||||
description: printer.jobs.length==1 ? printer.jobs.length+" "+qsTr("job") : printer.jobs.length+" "+qsTr("jobs")
|
||||
}
|
||||
|
||||
|
@ -64,7 +62,6 @@ Page {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: Theme.highlightColor
|
||||
text: printer.jobs[index]["job-id"].value
|
||||
Component.onCompleted: console.log(JSON.stringify(printer.jobs))
|
||||
}
|
||||
|
||||
Column {
|
||||
|
@ -73,7 +70,6 @@ Page {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
Label {
|
||||
text: printer.jobs[index]["job-name"] ? printer.jobs[index]["job-name"].value : qsTr("Untitled job")
|
||||
Component.onCompleted: console.log(JSON.stringify(printer.jobs))
|
||||
}
|
||||
Label {
|
||||
font.pixelSize: Theme.fontSizeTiny
|
||||
|
|
|
@ -65,7 +65,7 @@ Page {
|
|||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: qsTr("Install calligra:") + " " + (ConvertChecker.calligra ? qsTr("(done)") : "")
|
||||
text: qsTr("Install Calligra:") + " " + (ConvertChecker.calligra ? qsTr("(done)") : "")
|
||||
}
|
||||
|
||||
Label {
|
||||
|
@ -78,6 +78,16 @@ Page {
|
|||
Clipper {}
|
||||
}
|
||||
|
||||
Label {
|
||||
visible: appWin.expectCalligra
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: qsTr("If you are still seeing this message despite installing Calligra, that can be because of SailJail "+
|
||||
"in Sailfish OS 4.x before the compatibility permission in 4.4. " +
|
||||
"You can either upgrade to 4.4 or edit out the SailJail section in /usr/share/applications/harbour-seaprint.desktop")
|
||||
}
|
||||
|
||||
Item{
|
||||
width: parent.width
|
||||
height: Theme.itemSizeMedium
|
||||
|
|
|
@ -47,7 +47,7 @@ Page {
|
|||
}
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Clear default settings")
|
||||
text: qsTr("Reset default settings")
|
||||
visible: printer.attrs.hasOwnProperty("printer-uuid")
|
||||
onClicked: {
|
||||
db.removeJobSettings(printer.attrs["printer-uuid"].value, selectedFileType);
|
||||
|
@ -56,12 +56,12 @@ Page {
|
|||
}
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Save default settings")
|
||||
text: qsTr("Save as default settings")
|
||||
visible: printer.attrs.hasOwnProperty("printer-uuid")
|
||||
onClicked: {
|
||||
var tmp = jobParams;
|
||||
// Varies between documents, would be confusing to save
|
||||
tmp["page-ranges"] = undefined;
|
||||
delete tmp["page-ranges"];
|
||||
db.setJobSettings(printer.attrs["printer-uuid"].value, selectedFileType, JSON.stringify(tmp))
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,6 @@ Page {
|
|||
MenuItem {
|
||||
text: qsTr("Print")
|
||||
onClicked: {
|
||||
console.log(JSON.stringify(jobParams))
|
||||
pageStack.replace(Qt.resolvedUrl("BusyPage.qml"),{printer:printer},
|
||||
PageStackAction.Immediate)
|
||||
printer.print(jobParams, page.selectedFile)
|
||||
|
@ -85,7 +84,7 @@ Page {
|
|||
|
||||
PageHeader {
|
||||
id: pageHeader
|
||||
title: printer.attrs["printer-name"].value
|
||||
title: Utils.unknownForEmptyString(printer.attrs["printer-name"].value)
|
||||
description: Utils.basename(selectedFile)
|
||||
}
|
||||
|
||||
|
@ -105,6 +104,8 @@ Page {
|
|||
tag: IppMsg.Integer
|
||||
name: "copies"
|
||||
prettyName: qsTr("Copies")
|
||||
valid: _valid || Utils.supports_raster(printer)
|
||||
minimum_high: 99
|
||||
}
|
||||
ChoiceSetting {
|
||||
tag: IppMsg.Keyword
|
||||
|
@ -119,6 +120,7 @@ Page {
|
|||
|
||||
property var pdfpages: ConvertChecker.pdfPages(selectedFile)
|
||||
high: pdfpages == 0 ? 65535 : pdfpages
|
||||
acceptRangeList: true
|
||||
}
|
||||
ChoiceSetting {
|
||||
tag: IppMsg.Integer
|
||||
|
@ -149,10 +151,10 @@ Page {
|
|||
tag: IppMsg.Keyword
|
||||
name: "print-scaling"
|
||||
prettyName: qsTr("Scaling")
|
||||
valid: _valid && selectedFileType == Mimer.JPEG
|
||||
valid: _valid && Mimer.isImage(selectedFileType) && selectedFileType != Mimer.SVG
|
||||
DependentOn {
|
||||
target: transferFormatSetting
|
||||
values: [Mimer.JPEG]
|
||||
values: [Mimer.JPEG, Mimer.PNG]
|
||||
}
|
||||
}
|
||||
ChoiceSetting {
|
||||
|
@ -197,6 +199,7 @@ Page {
|
|||
name: "media-top-margin"
|
||||
subkey: "media-col"
|
||||
prettyName: qsTr("Top")
|
||||
valid: _valid && Mimer.isImage(selectedFileType)
|
||||
}
|
||||
ChoiceSetting {
|
||||
visible: marginsButton.active
|
||||
|
@ -204,6 +207,7 @@ Page {
|
|||
name: "media-bottom-margin"
|
||||
subkey: "media-col"
|
||||
prettyName: qsTr("Bottom")
|
||||
valid: _valid && Mimer.isImage(selectedFileType)
|
||||
}
|
||||
ChoiceSetting {
|
||||
visible: marginsButton.active
|
||||
|
@ -211,6 +215,7 @@ Page {
|
|||
name: "media-left-margin"
|
||||
subkey: "media-col"
|
||||
prettyName: qsTr("Left")
|
||||
valid: _valid && Mimer.isImage(selectedFileType)
|
||||
}
|
||||
ChoiceSetting {
|
||||
visible: marginsButton.active
|
||||
|
@ -218,6 +223,7 @@ Page {
|
|||
name: "media-right-margin"
|
||||
subkey: "media-col"
|
||||
prettyName: qsTr("Right")
|
||||
valid: _valid && Mimer.isImage(selectedFileType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,23 @@ Page {
|
|||
}
|
||||
}
|
||||
|
||||
TextSwitch {
|
||||
text: qsTr("Enable debug log")
|
||||
description: qsTr("Print debug log messages to console (if launched from there) or journal if launched normally. Takes effect on app (re-)start.")
|
||||
checked: debugLogSetting.value
|
||||
onCheckedChanged: {
|
||||
debugLogSetting.value = checked
|
||||
}
|
||||
}
|
||||
|
||||
TextSwitch {
|
||||
text: qsTr("Allow external connections")
|
||||
description: qsTr("Allow SeaPrint to fetch recources like icons and option name dictionaries from addresses that are not the printer itself. Some printer makers store icons on their website as opposed to locally on the printer.")
|
||||
checked: allowExternalConnectionsSetting.value
|
||||
onCheckedChanged: {
|
||||
allowExternalConnectionsSetting.value = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1148
qml/pages/strings.js
Normal file
1148
qml/pages/strings.js
Normal file
File diff suppressed because it is too large
Load diff
1268
qml/pages/utils.js
1268
qml/pages/utils.js
File diff suppressed because it is too large
Load diff
|
@ -4,15 +4,14 @@ Summary: SeaPrint
|
|||
Version: 0
|
||||
Release: 1
|
||||
Group: Qt/Qt
|
||||
License: LICENSE
|
||||
URL: http://example.org/
|
||||
License: GPLv3
|
||||
URL: https://github.com/attah/harbour-seaprint
|
||||
Source0: %{name}-%{version}.tar.bz2
|
||||
Requires: sailfishsilica-qt5 >= 0.10.9
|
||||
BuildRequires: pkgconfig(sailfishapp) >= 1.0.2
|
||||
BuildRequires: pkgconfig(Qt5Core)
|
||||
BuildRequires: pkgconfig(Qt5Qml)
|
||||
BuildRequires: pkgconfig(Qt5Quick)
|
||||
BuildRequires: pkgconfig(Qt5Svg)
|
||||
BuildRequires: pkgconfig(mlite5)
|
||||
BuildRequires: desktop-file-utils
|
||||
BuildRequires: pkgconfig(libcurl)
|
||||
|
@ -20,6 +19,10 @@ BuildRequires: pkgconfig(poppler)
|
|||
BuildRequires: pkgconfig(poppler-glib)
|
||||
BuildRequires: pkgconfig(glib-2.0)
|
||||
BuildRequires: pkgconfig(cairo)
|
||||
BuildRequires: pkgconfig(libjpeg)
|
||||
|
||||
%define _source_payload w6T.xzdio
|
||||
%define _binary_payload w6T.xzdio
|
||||
|
||||
%description
|
||||
Network printing for Sailfish OS
|
||||
|
@ -44,7 +47,7 @@ desktop-file-install --delete-original \
|
|||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%{_bindir}
|
||||
%{_bindir}/%{name}
|
||||
%{_datadir}/%{name}
|
||||
%{_datadir}/applications/%{name}.desktop
|
||||
%{_datadir}/icons/hicolor/*/apps/%{name}.png
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "convertchecker.h"
|
||||
#include <QProcess>
|
||||
#include <QtDebug>
|
||||
#include "mimer.h"
|
||||
|
||||
ConvertChecker::ConvertChecker() : poppler("libpoppler-glib.so.8")
|
||||
{
|
||||
|
|
|
@ -1,585 +0,0 @@
|
|||
#include "convertworker.h"
|
||||
#include <sailfishapp.h>
|
||||
#include "papersizes.h"
|
||||
#include "convertchecker.h"
|
||||
#include "mimer.h"
|
||||
#include "seaprint_version.h"
|
||||
#include <QImage>
|
||||
#include <QMatrix>
|
||||
#include <QPainter>
|
||||
#include <QTextDocument>
|
||||
#include <QPdfWriter>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include "ippprinter.h"
|
||||
#include "pdf2printable.h"
|
||||
#include "ppm2pwg.h"
|
||||
|
||||
#define OK(call) if(!(call)) throw ConvertFailedException()
|
||||
|
||||
ConvertWorker::ConvertWorker(IppPrinter* parent) // : QObject((QObject*)parent) borks multithereading?!
|
||||
{
|
||||
_printer = parent;
|
||||
}
|
||||
|
||||
void ConvertWorker::command(QByteArray msg)
|
||||
{
|
||||
CurlRequester cr(_printer->httpUrl());
|
||||
cr.setFinishedCallback(_printer, &IppPrinter::getPrinterAttributesFinished);
|
||||
|
||||
qDebug() << "command...";
|
||||
|
||||
cr.write(msg.data(), msg.length());
|
||||
}
|
||||
|
||||
// TODO: de-duplicate
|
||||
void ConvertWorker::getJobs(QByteArray msg)
|
||||
{
|
||||
CurlRequester cr(_printer->httpUrl());
|
||||
cr.setFinishedCallback(_printer, &IppPrinter::getJobsRequestFinished);
|
||||
|
||||
cr.write(msg.data(), msg.length());
|
||||
}
|
||||
|
||||
void ConvertWorker::cancelJob(QByteArray msg)
|
||||
{
|
||||
CurlRequester cr(_printer->httpUrl());
|
||||
cr.setFinishedCallback(_printer, &IppPrinter::cancelJobFinished);
|
||||
|
||||
cr.write(msg.data(), msg.length());
|
||||
}
|
||||
|
||||
void ConvertWorker::justUpload(QString filename, QByteArray header)
|
||||
{
|
||||
try {
|
||||
emit busyMessage(tr("Printing"));
|
||||
|
||||
CurlRequester cr(_printer->httpUrl());
|
||||
cr.setFinishedCallback(_printer, &IppPrinter::printRequestFinished);
|
||||
|
||||
QFile file(filename);
|
||||
file.open(QFile::ReadOnly);
|
||||
|
||||
OK(cr.write(header.data(), header.length()));
|
||||
QByteArray tmp = file.readAll();
|
||||
OK(cr.write(tmp.data(), tmp.length()));
|
||||
file.close();
|
||||
}
|
||||
catch(const ConvertFailedException& e)
|
||||
{
|
||||
emit failed(e.what() == QString("") ? tr("Upload error") : e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertWorker::convertPdf(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
|
||||
quint32 PageRangeLow, quint32 PageRangeHigh, bool BackHFlip, bool BackVFlip)
|
||||
{
|
||||
try {
|
||||
Format format;
|
||||
|
||||
emit busyMessage(tr("Printing"));
|
||||
|
||||
if(targetFormat == Mimer::URF)
|
||||
{
|
||||
format = Format::URF;
|
||||
}
|
||||
else if(targetFormat == Mimer::PWG)
|
||||
{
|
||||
format = Format::PWG;
|
||||
}
|
||||
else if(targetFormat == Mimer::Postscript)
|
||||
{
|
||||
format = Format::Postscript;
|
||||
}
|
||||
else if (targetFormat == Mimer::PDF)
|
||||
{
|
||||
format = Format::PDF;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ConvertFailedException(tr("Unsupported target format"));
|
||||
}
|
||||
|
||||
if(Colors == 0)
|
||||
{
|
||||
Colors = 3;
|
||||
}
|
||||
|
||||
CurlRequester cr(_printer->httpUrl());
|
||||
cr.setFinishedCallback(_printer, &IppPrinter::printRequestFinished);
|
||||
|
||||
OK(cr.write(header.data(), header.length()));
|
||||
|
||||
write_fun WriteFun([&cr](unsigned char const* buf, unsigned int len) -> bool
|
||||
{
|
||||
if(len == 0)
|
||||
return true;
|
||||
return cr.write((const char*)buf, len);
|
||||
});
|
||||
|
||||
progress_fun ProgressFun([this](size_t page, size_t total) -> void
|
||||
{
|
||||
emit progress(page, total);
|
||||
});
|
||||
|
||||
if(!PaperSizes.contains(PaperSize))
|
||||
{
|
||||
qDebug() << "Unsupported paper size" << PaperSize;
|
||||
throw ConvertFailedException(tr("Unsupported paper size"));
|
||||
}
|
||||
QSizeF size = PaperSizes[PaperSize];
|
||||
float Width = size.width();
|
||||
float Height = size.height();
|
||||
|
||||
int res = pdf_to_printable(filename.toStdString(), WriteFun, Colors, Quality, PaperSize.toStdString(), Width, Height, HwResX, HwResY,
|
||||
format, TwoSided, Tumble, BackHFlip, BackVFlip, PageRangeLow, PageRangeHigh, ProgressFun);
|
||||
|
||||
if(res != 0)
|
||||
{
|
||||
throw ConvertFailedException(tr("Conversion failed"));
|
||||
}
|
||||
|
||||
qDebug() << "Finished";
|
||||
}
|
||||
catch(const ConvertFailedException& e)
|
||||
{
|
||||
emit failed(e.what() == QString("") ? tr("Conversion error") : e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertWorker::convertImage(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, QMargins margins)
|
||||
{
|
||||
try {
|
||||
|
||||
bool urf = false;
|
||||
bool pdfOrPostscript = false;
|
||||
QString imageFormat = "";
|
||||
QStringList supportedImageFormats = {Mimer::JPEG, Mimer::PNG};
|
||||
|
||||
if(targetFormat == Mimer::URF)
|
||||
{
|
||||
urf = true;
|
||||
}
|
||||
else if(targetFormat == Mimer::PWG)
|
||||
{
|
||||
//ok
|
||||
}
|
||||
else if(targetFormat == Mimer::PDF || targetFormat == Mimer::Postscript)
|
||||
{
|
||||
HwResX = HwResY = std::min(HwResX, HwResY);
|
||||
pdfOrPostscript = true;
|
||||
}
|
||||
else if(supportedImageFormats.contains(targetFormat))
|
||||
{
|
||||
imageFormat = targetFormat.split("/")[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ConvertFailedException(tr("Unsupported target format"));
|
||||
}
|
||||
|
||||
if(Colors == 0)
|
||||
{
|
||||
Colors = 3;
|
||||
}
|
||||
|
||||
if(urf && (HwResX != HwResY))
|
||||
{ // URF only supports symmetric resolutions
|
||||
qDebug() << "Unsupported URF resolution" << PaperSize;
|
||||
throw ConvertFailedException(tr("Unsupported resolution (dpi)"));
|
||||
}
|
||||
|
||||
if(!PaperSizes.contains(PaperSize))
|
||||
{
|
||||
qDebug() << "Unsupported paper size" << PaperSize;
|
||||
throw ConvertFailedException(tr("Unsupported paper size"));
|
||||
}
|
||||
QSizeF size = PaperSizes[PaperSize];
|
||||
quint32 Width = qRound(size.width()/25.4*HwResX);
|
||||
quint32 Height = qRound(size.height()/25.4*HwResY);
|
||||
|
||||
qDebug() << "Size is" << Width << "x" << Height;
|
||||
|
||||
QImage inImage;
|
||||
if(!inImage.load(filename))
|
||||
{
|
||||
qDebug() << "failed to load";
|
||||
throw ConvertFailedException(tr("Failed to load image"));
|
||||
}
|
||||
|
||||
if(inImage.width() > inImage.height())
|
||||
{
|
||||
inImage = inImage.transformed(QMatrix().rotate(270.0));
|
||||
}
|
||||
|
||||
int leftMarginPx = (margins.left()/2540.0)*HwResX;
|
||||
int rightMarginPx = (margins.right()/2540.0)*HwResX;
|
||||
int topMarginPx = (margins.top()/2540.0)*HwResY;
|
||||
int bottomMarginPx = (margins.bottom()/2540.0)*HwResY;
|
||||
|
||||
int totalXMarginPx = leftMarginPx+rightMarginPx;
|
||||
int totalYMarginPx = topMarginPx+bottomMarginPx;
|
||||
|
||||
inImage = inImage.scaled(Width-totalXMarginPx, Height-totalYMarginPx,
|
||||
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
if(pdfOrPostscript)
|
||||
{
|
||||
QTemporaryFile tmpPdfFile;
|
||||
tmpPdfFile.open();
|
||||
QPdfWriter pdfWriter(tmpPdfFile.fileName());
|
||||
pdfWriter.setCreator("SeaPrint " SEAPRINT_VERSION);
|
||||
QPageSize pageSize(size, QPageSize::Millimeter);
|
||||
pdfWriter.setPageSize(pageSize);
|
||||
pdfWriter.setResolution(HwResX);
|
||||
QPainter painter(&pdfWriter);
|
||||
int xOffset = ((pdfWriter.width()-totalXMarginPx)-inImage.width())/2 + leftMarginPx;
|
||||
int yOffset = ((pdfWriter.height()-totalYMarginPx)-inImage.height())/2 + topMarginPx;
|
||||
painter.drawImage(xOffset, yOffset, inImage);
|
||||
painter.end();
|
||||
|
||||
convertPdf(tmpPdfFile.fileName(), header, targetFormat, Colors, Quality, PaperSize,
|
||||
HwResX, HwResY, false, false, 0, 0, false, false);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
QImage outImage = QImage(Width, Height, inImage.format());
|
||||
outImage.fill(Qt::white);
|
||||
QPainter painter(&outImage);
|
||||
int xOffset = ((outImage.width()-totalXMarginPx)-inImage.width())/2 + leftMarginPx;
|
||||
int yOffset = ((outImage.height()-totalYMarginPx)-inImage.height())/2 + topMarginPx;
|
||||
painter.drawImage(xOffset, yOffset, inImage);
|
||||
painter.end();
|
||||
|
||||
QBuffer buf;
|
||||
buf.open(QIODevice::ReadWrite);
|
||||
Bytestream outBts;
|
||||
|
||||
if(imageFormat != "")
|
||||
{ // We are converting to a supported image format
|
||||
outImage.save(&buf, imageFormat.toStdString().c_str());
|
||||
buf.seek(0);
|
||||
outBts = Bytestream(buf.size());
|
||||
buf.read((char*)(outBts.raw()), buf.size());
|
||||
}
|
||||
else
|
||||
{ // We are converting to a raster format
|
||||
|
||||
bool gray = Colors == 0 ? inImage.allGray() : Colors == 1;
|
||||
|
||||
outImage.save(&buf, gray ? "pgm" : "ppm");
|
||||
buf.seek(0);
|
||||
// Skip header - TODO consider reimplementing
|
||||
buf.readLine(255);
|
||||
buf.readLine(255);
|
||||
buf.readLine(255);
|
||||
|
||||
Bytestream inBts(Width*Height*Colors);
|
||||
|
||||
if((buf.size()-buf.pos()) != inBts.size())
|
||||
{
|
||||
qDebug() << buf.size() << buf.pos() << inBts.size();
|
||||
throw ConvertFailedException();
|
||||
}
|
||||
|
||||
buf.read((char*)(inBts.raw()), inBts.size());
|
||||
|
||||
outBts << (urf ? make_urf_file_hdr(1) : make_pwg_file_hdr());
|
||||
bmp_to_pwg(inBts, outBts, urf, 1, Colors, Quality, HwResX, HwResY, Width, Height, false, false, PaperSize.toStdString(), false, false);
|
||||
}
|
||||
|
||||
emit busyMessage(tr("Printing"));
|
||||
|
||||
CurlRequester cr(_printer->httpUrl());
|
||||
cr.setFinishedCallback(_printer, &IppPrinter::printRequestFinished);
|
||||
|
||||
OK(cr.write(header.data(), header.length()));
|
||||
OK(cr.write((char*)(outBts.raw()), outBts.size()));
|
||||
}
|
||||
|
||||
qDebug() << "posted";
|
||||
|
||||
}
|
||||
catch(const ConvertFailedException& e)
|
||||
{
|
||||
emit failed(e.what() == QString("") ? tr("Conversion error") : e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertWorker::convertOfficeDocument(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
|
||||
quint32 PageRangeLow, quint32 PageRangeHigh, bool BackHFlip, bool BackVFlip)
|
||||
{
|
||||
try {
|
||||
|
||||
if(targetFormat == Mimer::URF && (HwResX != HwResY))
|
||||
{ // URF only supports symmetric resolutions
|
||||
qDebug() << "Unsupported URF resolution" << PaperSize;
|
||||
throw ConvertFailedException(tr("Unsupported resolution (dpi)"));
|
||||
}
|
||||
|
||||
QString ShortPaperSize;
|
||||
if(CalligraPaperSizes.contains(PaperSize))
|
||||
{
|
||||
ShortPaperSize = CalligraPaperSizes[PaperSize];
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Unsupported PDF paper size" << PaperSize;
|
||||
throw ConvertFailedException(tr("Unsupported PDF paper size"));
|
||||
}
|
||||
|
||||
QProcess CalligraConverter(this);
|
||||
CalligraConverter.setProgram("calligraconverter");
|
||||
QStringList CalligraConverterArgs = {"--batch", "--mimetype", Mimer::PDF, "--print-orientation", "Portrait", "--print-papersize", ShortPaperSize};
|
||||
|
||||
CalligraConverterArgs << filename;
|
||||
|
||||
QTemporaryFile tmpPdfFile;
|
||||
tmpPdfFile.open();
|
||||
CalligraConverterArgs << tmpPdfFile.fileName();
|
||||
|
||||
qDebug() << "CalligraConverteArgs is" << CalligraConverterArgs;
|
||||
CalligraConverter.setArguments(CalligraConverterArgs);
|
||||
|
||||
CalligraConverter.start();
|
||||
|
||||
qDebug() << "CalligraConverter Starting";
|
||||
|
||||
if(!CalligraConverter.waitForStarted())
|
||||
{
|
||||
qDebug() << "CalligraConverter died";
|
||||
throw ConvertFailedException();
|
||||
}
|
||||
|
||||
qDebug() << "CalligraConverter Started";
|
||||
|
||||
if(!CalligraConverter.waitForFinished(-1))
|
||||
{
|
||||
qDebug() << "CalligraConverter failed";
|
||||
throw ConvertFailedException();
|
||||
}
|
||||
|
||||
// qDebug() << CalligraConverter->readAllStandardError();
|
||||
|
||||
quint32 pages = ConvertChecker::instance()->pdfPages(tmpPdfFile.fileName());
|
||||
if (!pages)
|
||||
{
|
||||
qDebug() << "pdfinfo returned 0 pages";
|
||||
throw ConvertFailedException(tr("Failed to get info about PDF file"));
|
||||
}
|
||||
|
||||
if(PageRangeLow==0)
|
||||
{
|
||||
PageRangeLow=1;
|
||||
}
|
||||
|
||||
if(PageRangeHigh==0 || PageRangeHigh > pages)
|
||||
{
|
||||
PageRangeHigh=pages;
|
||||
}
|
||||
|
||||
// Actual number of pages to print
|
||||
pages = PageRangeHigh-PageRangeLow+1;
|
||||
|
||||
qDebug() << "PageRangeLow" << PageRangeLow << "PageRangeHigh" << PageRangeHigh << "pages" << pages;
|
||||
|
||||
convertPdf(tmpPdfFile.fileName(), header, targetFormat, Colors, Quality, PaperSize, HwResX, HwResY, TwoSided, Tumble,
|
||||
PageRangeLow, PageRangeHigh, BackHFlip, BackVFlip);
|
||||
|
||||
qDebug() << "Finished";
|
||||
|
||||
qDebug() << "posted";
|
||||
|
||||
}
|
||||
catch(const ConvertFailedException& e)
|
||||
{
|
||||
emit failed(e.what() == QString("") ? tr("Conversion error") : e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertWorker::convertPlaintext(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
|
||||
bool BackHFlip, bool BackVFlip)
|
||||
{
|
||||
try {
|
||||
|
||||
if(!PaperSizes.contains(PaperSize))
|
||||
{
|
||||
qDebug() << "Unsupported paper size" << PaperSize;
|
||||
throw ConvertFailedException(tr("Unsupported paper size"));
|
||||
}
|
||||
QSizeF size = PaperSizes[PaperSize];
|
||||
|
||||
QFile inFile(filename);
|
||||
if(!inFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
throw ConvertFailedException(tr("Failed to open file"));
|
||||
}
|
||||
|
||||
quint32 resolution = std::min(HwResX, HwResY);
|
||||
|
||||
QTemporaryFile tmpPdfFile;
|
||||
tmpPdfFile.open();
|
||||
|
||||
QPdfWriter pdfWriter(tmpPdfFile.fileName());
|
||||
pdfWriter.setCreator("SeaPrint " SEAPRINT_VERSION);
|
||||
QPageSize pageSize(size, QPageSize::Millimeter);
|
||||
pdfWriter.setPageSize(pageSize);
|
||||
pdfWriter.setResolution(resolution);
|
||||
|
||||
qreal docHeight = pageSize.sizePixels(resolution).height();
|
||||
|
||||
QTextDocument doc;
|
||||
|
||||
QFont font = QFont("Courier");
|
||||
font.setPointSizeF(1);
|
||||
|
||||
qreal charHeight = 0;
|
||||
|
||||
// Find the optimal font size
|
||||
while(true) {
|
||||
QFont tmpFont = font;
|
||||
tmpFont.setPointSizeF(font.pointSizeF()+0.5);
|
||||
QFontMetricsF qfm(tmpFont, &pdfWriter);
|
||||
|
||||
charHeight = qfm.lineSpacing();
|
||||
|
||||
if(charHeight*66 > docHeight)
|
||||
{
|
||||
break;
|
||||
}
|
||||
font=tmpFont;
|
||||
}
|
||||
|
||||
QFontMetricsF qfm(font, &pdfWriter);
|
||||
|
||||
charHeight = qfm.height();
|
||||
|
||||
int textHeight = 60*charHeight;
|
||||
qreal margin = ((docHeight-textHeight)/2);
|
||||
qreal mmMargin = margin/(resolution/25.4);
|
||||
|
||||
doc.setDefaultFont(font);
|
||||
(void)doc.documentLayout(); // wat
|
||||
|
||||
|
||||
// Needs to be before painter
|
||||
pdfWriter.setMargins({mmMargin, mmMargin, mmMargin, mmMargin});
|
||||
|
||||
QPainter painter(&pdfWriter);
|
||||
|
||||
doc.documentLayout()->setPaintDevice(painter.device());
|
||||
doc.setDocumentMargin(margin);
|
||||
|
||||
// Hack to make the document and pdfWriter margins overlap
|
||||
// Apparently calls to painter.translate() stack... who knew!
|
||||
painter.translate(-margin, -margin);
|
||||
|
||||
QRectF body = pageSize.rectPixels(resolution);
|
||||
doc.setPageSize(body.size());
|
||||
|
||||
QString allText = inFile.readAll();
|
||||
if(allText.startsWith("\f"))
|
||||
{
|
||||
allText.remove(0, 1);
|
||||
}
|
||||
|
||||
if(allText.endsWith("\f"))
|
||||
{
|
||||
allText.chop(1);
|
||||
}
|
||||
else if(allText.endsWith("\f\n"))
|
||||
{
|
||||
allText.chop(2);
|
||||
}
|
||||
|
||||
QStringList pages = allText.split('\f');
|
||||
|
||||
bool first = true;
|
||||
int pageCount = 0;
|
||||
|
||||
foreach(QString page, pages)
|
||||
{
|
||||
if(!first)
|
||||
{
|
||||
pdfWriter.newPage();
|
||||
}
|
||||
first = false;
|
||||
|
||||
if(page.endsWith("\n"))
|
||||
{
|
||||
page.chop(1);
|
||||
}
|
||||
doc.setPlainText(page);
|
||||
|
||||
int p = 0; // Page number in this document, starting from 0
|
||||
|
||||
while(true)
|
||||
{
|
||||
painter.translate(body.left(), body.top() - p*body.height());
|
||||
QRectF view(0, p*body.height(), body.width(), body.height());
|
||||
painter.setClipRect(view);
|
||||
|
||||
QAbstractTextDocumentLayout::PaintContext context;
|
||||
context.clip = view;
|
||||
context.palette.setColor(QPalette::Text, Qt::black);
|
||||
doc.documentLayout()->draw(&painter, context);
|
||||
|
||||
p++;
|
||||
pageCount++;
|
||||
|
||||
if(p >= doc.pageCount())
|
||||
break;
|
||||
|
||||
pdfWriter.newPage();
|
||||
}
|
||||
}
|
||||
|
||||
painter.end();
|
||||
|
||||
convertPdf(tmpPdfFile.fileName(), header, targetFormat, Colors, Quality, PaperSize, HwResX, HwResY,
|
||||
TwoSided, Tumble, 0, 0, BackHFlip, BackVFlip);
|
||||
|
||||
qDebug() << "Finished";
|
||||
qDebug() << "posted";
|
||||
|
||||
}
|
||||
catch(const ConvertFailedException& e)
|
||||
{
|
||||
emit failed(e.what() == QString("") ? tr("Conversion error") : e.what());
|
||||
}
|
||||
}
|
||||
|
||||
QString ConvertWorker::getPopplerShortPaperSize(QString PaperSize)
|
||||
{
|
||||
QString ShortPaperSize;
|
||||
if(PaperSize == "iso_a4_210x297mm")
|
||||
{
|
||||
ShortPaperSize = "A4";
|
||||
}
|
||||
else if (PaperSize == "iso_a3_297x420mm")
|
||||
{
|
||||
ShortPaperSize = "A3";
|
||||
}
|
||||
else if (PaperSize == "na_letter_8.5x11in")
|
||||
{
|
||||
ShortPaperSize = "letter";
|
||||
}
|
||||
else if (PaperSize == "na_legal_8.5x14in")
|
||||
{
|
||||
ShortPaperSize = "legal";
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Unsupported PDF paper size" << PaperSize;
|
||||
throw ConvertFailedException(tr("Unsupported PDF paper size"));
|
||||
}
|
||||
return ShortPaperSize;
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
#ifndef CONVERTWORKER_H
|
||||
#define CONVERTWORKER_H
|
||||
#include <QObject>
|
||||
#include <QtNetwork>
|
||||
#include "curlrequester.h"
|
||||
|
||||
class IppPrinter;
|
||||
|
||||
class ConvertFailedException: public std::exception
|
||||
{
|
||||
private:
|
||||
QString _msg;
|
||||
public:
|
||||
ConvertFailedException(QString msg = "") : _msg(msg = msg)
|
||||
{
|
||||
}
|
||||
virtual const char* what() const throw()
|
||||
{
|
||||
return _msg.toStdString().c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConvertWorker(IppPrinter* parent);
|
||||
|
||||
private:
|
||||
ConvertWorker();
|
||||
|
||||
public slots:
|
||||
void command(QByteArray msg);
|
||||
void getJobs(QByteArray msg);
|
||||
void cancelJob(QByteArray msg);
|
||||
|
||||
void justUpload(QString filename, QByteArray header);
|
||||
|
||||
void convertPdf(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
|
||||
quint32 PageRangeLow, quint32 PageRangeHigh, bool BackHFlip, bool BackVFlip);
|
||||
|
||||
void convertImage(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, QMargins margins);
|
||||
|
||||
void convertOfficeDocument(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
|
||||
quint32 PageRangeLow, quint32 PageRangeHigh, bool BackHFlip, bool BackVFlip);
|
||||
|
||||
void convertPlaintext(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble, bool BackHFlip, bool BackVFlip);
|
||||
|
||||
signals:
|
||||
void done(QNetworkRequest request, QTemporaryFile* data);
|
||||
void progress(qint64 done, qint64 pages);
|
||||
void busyMessage(QString msg);
|
||||
void failed(QString message);
|
||||
|
||||
private:
|
||||
|
||||
IppPrinter* _printer;
|
||||
|
||||
QString getPopplerShortPaperSize(QString PaperSize);
|
||||
};
|
||||
|
||||
#endif // CONVERTWORKER_H
|
|
@ -1,28 +1,104 @@
|
|||
#include "curlrequester.h"
|
||||
#include <algorithm>
|
||||
#include "settings.h"
|
||||
|
||||
CurlRequester::CurlRequester(QUrl addr) : _addr(addr), _canWrite(1), _canRead(), _reading(false), _done(false), _dest(nullptr), _size(0), _offset(0), _performer(addr, this)
|
||||
static size_t trampoline(char* dest, size_t size, size_t nmemb, void* userp)
|
||||
{
|
||||
_performer.start();
|
||||
CurlRequester* cid = (CurlRequester*)userp;
|
||||
return cid->requestWrite(dest, size*nmemb);
|
||||
}
|
||||
|
||||
CurlRequester::CurlRequester(QUrl addr, Role role, Bytestream* singleData)
|
||||
: _addr(addr), _canWrite(1), _canRead(), _reading(false), _done(false), _dest(nullptr), _size(0), _offset(0), _curl(curl_easy_init())
|
||||
{
|
||||
|
||||
curl_easy_setopt(_curl, CURLOPT_URL, addr.toString().toStdString().c_str());
|
||||
|
||||
bool verbose = QLoggingCategory::defaultCategory()->isDebugEnabled();
|
||||
|
||||
curl_easy_setopt(_curl, CURLOPT_VERBOSE, verbose);
|
||||
if(Settings::instance()->ignoreSslErrors())
|
||||
{
|
||||
curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYSTATUS, 0L);
|
||||
}
|
||||
|
||||
_opts = NULL;
|
||||
_opts = curl_slist_append(_opts, "User-Agent: SeaPrint " SEAPRINT_VERSION);
|
||||
|
||||
|
||||
switch (role) {
|
||||
case IppRequest:
|
||||
{
|
||||
curl_easy_setopt(_curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(_curl, CURLOPT_READFUNCTION, trampoline);
|
||||
curl_easy_setopt(_curl, CURLOPT_READDATA, this);
|
||||
|
||||
_opts = curl_slist_append(_opts, "Expect:");
|
||||
if(singleData != nullptr)
|
||||
{
|
||||
curl_easy_setopt(_curl, CURLOPT_POSTFIELDSIZE, singleData->size());
|
||||
write((char*)(singleData->raw()), singleData->size());
|
||||
}
|
||||
else
|
||||
{
|
||||
_opts = curl_slist_append(_opts, "Transfer-Encoding: chunked");
|
||||
}
|
||||
_opts = curl_slist_append(_opts, "Content-Type: application/ipp");
|
||||
_opts = curl_slist_append(_opts, "Accept-Encoding: identity");
|
||||
break;
|
||||
}
|
||||
case HttpGetRequest:
|
||||
{
|
||||
curl_easy_setopt(_curl, CURLOPT_HTTPGET, 1L);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _opts);
|
||||
|
||||
_worker = QtConcurrent::run([this](){
|
||||
Bytestream buf;
|
||||
curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &buf);
|
||||
curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
|
||||
CURLcode res = curl_easy_perform(_curl);
|
||||
if(res != CURLE_OK)
|
||||
qDebug() << "curl_easy_perform() failed: " << curl_easy_strerror(res);
|
||||
|
||||
_result = res;
|
||||
_resultMsg = buf;
|
||||
});
|
||||
}
|
||||
|
||||
CurlRequester::~CurlRequester()
|
||||
{
|
||||
while(!_canWrite.tryAcquire(1, 500))
|
||||
{
|
||||
if(!_performer.isRunning())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
_done = true;
|
||||
_canRead.release();
|
||||
_performer.wait();
|
||||
await();
|
||||
|
||||
if(_dest != nullptr)
|
||||
{
|
||||
delete _dest;
|
||||
}
|
||||
|
||||
curl_slist_free_all(_opts);
|
||||
curl_easy_cleanup(_curl);
|
||||
}
|
||||
|
||||
CURLcode CurlRequester::await(Bytestream* data)
|
||||
{
|
||||
while(_worker.isRunning() && !_canWrite.tryAcquire(1, 500))
|
||||
{}
|
||||
|
||||
_done = true;
|
||||
_canRead.release();
|
||||
_worker.waitForFinished();
|
||||
|
||||
if(data != nullptr)
|
||||
{
|
||||
(*data) = _resultMsg;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
|
||||
bool CurlRequester::write(const char *data, size_t size)
|
||||
|
@ -30,7 +106,7 @@ bool CurlRequester::write(const char *data, size_t size)
|
|||
qDebug() << "write " << size;
|
||||
while(!_canWrite.tryAcquire(1, 500))
|
||||
{
|
||||
if(!_performer.isRunning())
|
||||
if(!_worker.isRunning())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -53,7 +129,7 @@ size_t CurlRequester::requestWrite(char* dest, size_t size)
|
|||
if(!_reading)
|
||||
{
|
||||
_canRead.acquire();
|
||||
if(_done) // Can only have been set by write() - only relevant to check if strating to write
|
||||
if(_done) // Can only have been set by await() - only relevant to check if strating to write
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,30 +4,55 @@
|
|||
#include <QUrl>
|
||||
#include <QThread>
|
||||
#include <QSemaphore>
|
||||
#include <QMetaMethod>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <curl/curl.h>
|
||||
#include <bytestream.h>
|
||||
#include <QDebug>
|
||||
#include "curlworker.h"
|
||||
#include <functional>
|
||||
|
||||
class CurlRequester : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CurlRequester(QUrl addr);
|
||||
enum Role {
|
||||
IppRequest,
|
||||
HttpGetRequest
|
||||
};
|
||||
|
||||
CurlRequester(QUrl addr, Role role = IppRequest, Bytestream* = nullptr);
|
||||
~CurlRequester();
|
||||
|
||||
template<typename Class, typename Callback>
|
||||
bool setFinishedCallback(const Class* receiverObject, Callback cb)
|
||||
{
|
||||
connect(&_performer, &CurlWorker::done, receiverObject, cb);
|
||||
return true;
|
||||
}
|
||||
CURLcode await(Bytestream* = nullptr);
|
||||
|
||||
bool write(const char *data, size_t size);
|
||||
size_t requestWrite(char* dest, size_t size);
|
||||
|
||||
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
size_t bytes_to_write = size*nmemb;
|
||||
((Bytestream*)userdata)->putBytes(ptr, bytes_to_write);
|
||||
return bytes_to_write;
|
||||
}
|
||||
|
||||
private:
|
||||
CurlRequester();
|
||||
|
||||
// Container for the cURL global init and cleanup
|
||||
class GlobalEnv
|
||||
{
|
||||
public:
|
||||
GlobalEnv()
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
}
|
||||
~GlobalEnv()
|
||||
{
|
||||
curl_global_cleanup();
|
||||
}
|
||||
};
|
||||
// Must be run exactly once, thus static
|
||||
static GlobalEnv _gEnv;
|
||||
|
||||
QUrl _addr;
|
||||
|
||||
QSemaphore _canWrite;
|
||||
|
@ -35,11 +60,19 @@ private:
|
|||
bool _reading;
|
||||
bool _done;
|
||||
|
||||
CURLcode _result;
|
||||
Bytestream _resultMsg;
|
||||
|
||||
char* _dest;
|
||||
size_t _size;
|
||||
size_t _offset;
|
||||
|
||||
CurlWorker _performer;
|
||||
friend class CurlWorker;
|
||||
|
||||
CURL* _curl;
|
||||
struct curl_slist* _opts = NULL;
|
||||
|
||||
QFuture<void> _worker;
|
||||
};
|
||||
|
||||
#endif // CURLREQUESTER_H
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
#include "curlworker.h"
|
||||
#include "curlrequester.h"
|
||||
#include "settings.h"
|
||||
#include <seaprint_version.h>
|
||||
|
||||
Q_DECLARE_METATYPE(CURLcode)
|
||||
|
||||
static size_t trampoline(char* dest, size_t size, size_t nmemb, void* userp)
|
||||
{
|
||||
CurlRequester* cid = (CurlRequester*)userp;
|
||||
return cid->requestWrite(dest, size*nmemb);
|
||||
}
|
||||
|
||||
CurlWorker::CurlWorker(QUrl addr, void* parent)
|
||||
{
|
||||
qRegisterMetaType<CURLcode>();
|
||||
|
||||
_curl = curl_easy_init();
|
||||
// if(!curl)
|
||||
// return false;
|
||||
|
||||
curl_easy_setopt(_curl, CURLOPT_URL, addr.toString().toStdString().c_str());
|
||||
curl_easy_setopt(_curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(_curl, CURLOPT_READFUNCTION, trampoline);
|
||||
curl_easy_setopt(_curl, CURLOPT_READDATA, parent);
|
||||
// curl_easy_setopt(_curl, CURLOPT_VERBOSE, 1L);
|
||||
if(Settings::instance()->ignoreSslErrors())
|
||||
{
|
||||
curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYSTATUS, 0L);
|
||||
}
|
||||
|
||||
_opts = NULL;
|
||||
|
||||
_opts = curl_slist_append(_opts, "Transfer-Encoding: chunked");
|
||||
_opts = curl_slist_append(_opts, "Content-Type: application/ipp");
|
||||
_opts = curl_slist_append(_opts, "User-Agent: SeaPrint " SEAPRINT_VERSION);
|
||||
_opts = curl_slist_append(_opts, "Accept-Encoding: identity");
|
||||
|
||||
curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _opts);
|
||||
|
||||
}
|
||||
|
||||
CurlWorker::~CurlWorker()
|
||||
{
|
||||
curl_slist_free_all(_opts);
|
||||
curl_easy_cleanup(_curl);
|
||||
}
|
||||
|
||||
void CurlWorker::run(){
|
||||
QByteArray buf;
|
||||
curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &buf);
|
||||
curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
|
||||
CURLcode res = curl_easy_perform(_curl);
|
||||
if(res != CURLE_OK)
|
||||
qDebug() << "curl_easy_perform() failed: " << curl_easy_strerror(res);
|
||||
|
||||
emit done(res, buf);
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
#ifndef CURLWORKER_H
|
||||
#define CURLWORKER_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QUrl>
|
||||
#include <curl/curl.h>
|
||||
#include <QtDebug>
|
||||
|
||||
class CurlWorker : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CurlWorker(QUrl addr, void* parent);
|
||||
~CurlWorker();
|
||||
|
||||
void run() override;
|
||||
|
||||
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
size_t bytes_to_write = size*nmemb;
|
||||
((QByteArray*)userdata)->append(ptr, bytes_to_write);
|
||||
return bytes_to_write;
|
||||
}
|
||||
signals:
|
||||
void done(CURLcode, QByteArray);
|
||||
|
||||
private:
|
||||
// Container for the cURL global init and cleanup
|
||||
class GlobalEnv
|
||||
{
|
||||
public:
|
||||
GlobalEnv()
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
}
|
||||
~GlobalEnv()
|
||||
{
|
||||
curl_global_cleanup();
|
||||
}
|
||||
};
|
||||
// Must be run exactly once, thus static
|
||||
static GlobalEnv _gEnv;
|
||||
|
||||
CurlWorker();
|
||||
CURL* _curl = nullptr;
|
||||
struct curl_slist* _opts;
|
||||
};
|
||||
|
||||
|
||||
#endif // CURLWORKER_H
|
25
src/dbusadaptorbase.cpp
Normal file
25
src/dbusadaptorbase.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "dbusadaptorbase.h"
|
||||
#include <QtQuick>
|
||||
|
||||
void DBusAdaptorBase::doActivate()
|
||||
{
|
||||
QMetaObject::invokeMethod(_view->rootObject(), "activate");
|
||||
}
|
||||
|
||||
void DBusAdaptorBase::doOpen(const QString& fileName)
|
||||
{
|
||||
QMetaObject::invokeMethod(_view->rootObject(), "openFile", Q_ARG(QVariant, fileName));
|
||||
doActivate();
|
||||
}
|
||||
|
||||
void DBusAdaptorBase::doCreateFileAndOpen(const QString& fileContents, const QString& name)
|
||||
{
|
||||
QTemporaryFile tmpfile(QDir::tempPath() + "/" + name);
|
||||
tmpfile.setAutoRemove(false);
|
||||
tmpfile.open();
|
||||
tmpfile.write(fileContents.toUtf8());
|
||||
tmpfile.close();
|
||||
QMetaObject::invokeMethod(_view->rootObject(), "openFile", Q_ARG(QVariant, tmpfile.fileName()));
|
||||
|
||||
doActivate();
|
||||
}
|
24
src/dbusadaptorbase.h
Normal file
24
src/dbusadaptorbase.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef DBUSADAPTORBASE_H
|
||||
#define DBUSADAPTORBASE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtDBus/QDBusAbstractAdaptor>
|
||||
#include <QQuickView>
|
||||
|
||||
class DBusAdaptorBase
|
||||
{
|
||||
public:
|
||||
DBusAdaptorBase(QQuickView* view)
|
||||
: _view(view)
|
||||
{}
|
||||
|
||||
void doActivate();
|
||||
void doOpen(const QString& fileName);
|
||||
void doCreateFileAndOpen(const QString& fileContents, const QString& name);
|
||||
|
||||
protected:
|
||||
QQuickView* _view;
|
||||
|
||||
};
|
||||
|
||||
#endif // DBUSADAPTORBASE_H
|
31
src/freedesktopdbusadaptor.cpp
Normal file
31
src/freedesktopdbusadaptor.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "freedesktopdbusadaptor.h"
|
||||
|
||||
#include <QtQuick>
|
||||
|
||||
FreedesktopDBusAdaptor::FreedesktopDBusAdaptor(QQuickView *view)
|
||||
: QDBusAbstractAdaptor(view)
|
||||
, DBusAdaptorBase(view)
|
||||
{
|
||||
}
|
||||
|
||||
FreedesktopDBusAdaptor::~FreedesktopDBusAdaptor()
|
||||
{
|
||||
}
|
||||
|
||||
void FreedesktopDBusAdaptor::Open(const QStringList& uris, const QVariantMap& platformData)
|
||||
{
|
||||
qDebug() << platformData;
|
||||
if (!uris.isEmpty() && uris[0] != "") {
|
||||
doOpen(uris.at(0));
|
||||
}
|
||||
else if(platformData.contains("data"))
|
||||
{
|
||||
doCreateFileAndOpen(platformData["data"].toString(),
|
||||
(platformData.contains("name") ? platformData["name"].toString() : "seaprint"));
|
||||
}
|
||||
}
|
||||
|
||||
void FreedesktopDBusAdaptor::Activate(const QVariantMap&)
|
||||
{
|
||||
QMetaObject::invokeMethod(_view->rootObject(), "activate");
|
||||
}
|
21
src/freedesktopdbusadaptor.h
Normal file
21
src/freedesktopdbusadaptor.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef FREEDESKTOPDBUSADAPTOR_H
|
||||
#define FREEDESKTOPDBUSADAPTOR_H
|
||||
|
||||
#include "dbusadaptorbase.h"
|
||||
|
||||
class FreedesktopDBusAdaptor : public QDBusAbstractAdaptor, public DBusAdaptorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Application")
|
||||
|
||||
public:
|
||||
FreedesktopDBusAdaptor(QQuickView *view);
|
||||
~FreedesktopDBusAdaptor();
|
||||
|
||||
public Q_SLOTS:
|
||||
Q_NOREPLY void Open(const QStringList& uris, const QVariantMap& platformData);
|
||||
Q_NOREPLY void Activate(const QVariantMap& platformData);
|
||||
|
||||
};
|
||||
|
||||
#endif // FREEDESKTOPDBUSADAPTOR_H
|
|
@ -1,13 +1,23 @@
|
|||
#include <QtQuick>
|
||||
#include <QVariant>
|
||||
#include <QDBusConnection>
|
||||
|
||||
#include <sailfishapp.h>
|
||||
#include <seaprint_version.h>
|
||||
#include <src/ippdiscovery.h>
|
||||
#include <src/ippprinter.h>
|
||||
#include <src/imageitem.h>
|
||||
#include <src/mimer.h>
|
||||
#include <src/convertchecker.h>
|
||||
#include <src/svgprovider.h>
|
||||
#include <src/settings.h>
|
||||
#include <src/rangelistchecker.h>
|
||||
#include <src/freedesktopdbusadaptor.h>
|
||||
#include <src/seaprintdbusadaptor.h>
|
||||
#include "argget.h"
|
||||
#include <iostream>
|
||||
|
||||
Q_DECLARE_METATYPE(CURLcode)
|
||||
Q_DECLARE_METATYPE(Bytestream)
|
||||
Q_DECLARE_METATYPE(PrintParameters)
|
||||
|
||||
template <class T>
|
||||
static QObject* singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
|
@ -22,6 +32,24 @@ static QObject* singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngi
|
|||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool prestart = false;
|
||||
std::string FileName;
|
||||
SwitchArg<bool> pretsartOpt(prestart, {"--prestart"}, "Run prestart");
|
||||
|
||||
PosArg fileArg(FileName, "File to print", true);
|
||||
ArgGet args({&pretsartOpt}, {&fileArg});
|
||||
if(!args.get_args(argc, argv))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
qRegisterMetaType<CURLcode>();
|
||||
qRegisterMetaType<Bytestream>();
|
||||
qRegisterMetaType<PrintParameters>();
|
||||
|
||||
// Turn on/off logging according to setting
|
||||
QLoggingCategory::defaultCategory()->setEnabled(QtMsgType::QtDebugMsg, Settings::instance()->debugLog());
|
||||
|
||||
QGuiApplication* app = SailfishApp::application(argc, argv);
|
||||
|
||||
app->setOrganizationName(QStringLiteral("net.attah"));
|
||||
|
@ -31,17 +59,36 @@ int main(int argc, char *argv[])
|
|||
qmlRegisterSingletonType<IppDiscovery>("seaprint.ippdiscovery", 1, 0, "IppDiscovery", singletontype_provider<IppDiscovery>);
|
||||
qmlRegisterSingletonType<Mimer>("seaprint.mimer", 1, 0, "Mimer", singletontype_provider<Mimer>);
|
||||
qmlRegisterSingletonType<ConvertChecker>("seaprint.convertchecker", 1, 0, "ConvertChecker", singletontype_provider<ConvertChecker>);
|
||||
qmlRegisterSingletonType<ConvertChecker>("seaprint.settings", 1, 0, "SeaPrintSettings", singletontype_provider<Settings>);
|
||||
qmlRegisterSingletonType<Settings>("seaprint.settings", 1, 0, "SeaPrintSettings", singletontype_provider<Settings>);
|
||||
qmlRegisterSingletonType<RangeListChecker>("seaprint.rangelistchecker", 1, 0, "RangeListChecker", singletontype_provider<RangeListChecker>);
|
||||
|
||||
qmlRegisterType<IppPrinter>("seaprint.ippprinter", 1, 0, "IppPrinter");
|
||||
qmlRegisterType<ImageItem>("seaprint.imageitem", 1, 0, "ImageItem");
|
||||
qmlRegisterUncreatableType<IppMsg>("seaprint.ippmsg", 1, 0, "IppMsg", "Only used to supply an enum type");
|
||||
|
||||
QQuickView* view = SailfishApp::createView();
|
||||
|
||||
view->engine()->addImportPath(SailfishApp::pathTo("qml/pages").toString());
|
||||
view->engine()->addImageProvider(QLatin1String("ippdiscovery"), IppDiscovery::instance());
|
||||
view->engine()->addImageProvider(QLatin1String("svg"), SvgProvider::instance());
|
||||
|
||||
view->setSource(SailfishApp::pathToMainQml());
|
||||
|
||||
FreedesktopDBusAdaptor freedesktopDbus(view);
|
||||
SeaPrintDBusAdaptor seaprintDbus(view);
|
||||
|
||||
if (!QDBusConnection::sessionBus().registerObject("/net/attah/seaprint", view))
|
||||
qWarning() << "Could not register /net/attah/seaprint D-Bus object.";
|
||||
|
||||
if (!QDBusConnection::sessionBus().registerService("net.attah.seaprint"))
|
||||
qWarning() << "Could not register net.attah.seaprint D-Bus service.";
|
||||
|
||||
if(!FileName.empty())
|
||||
{
|
||||
qDebug() << "Opening" << FileName.c_str();
|
||||
QVariant fileNameVariant(FileName.c_str());
|
||||
QMetaObject::invokeMethod(view->rootObject(), "openFile", Q_ARG(QVariant, fileNameVariant));
|
||||
}
|
||||
if(!prestart)
|
||||
{
|
||||
view->show();
|
||||
}
|
||||
return app->exec();
|
||||
}
|
||||
|
|
29
src/imageitem.cpp
Normal file
29
src/imageitem.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "imageitem.h"
|
||||
|
||||
ImageItem::ImageItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ImageItem::paint(QPainter *painter)
|
||||
{
|
||||
QImage scaled = _image.scaledToHeight(boundingRect().height(), Qt::SmoothTransformation);
|
||||
painter->drawImage(QPoint {0, 0}, scaled);
|
||||
}
|
||||
|
||||
QImage ImageItem::getImage() const
|
||||
{
|
||||
return _image;
|
||||
}
|
||||
|
||||
void ImageItem::setImage(const QImage &image)
|
||||
{
|
||||
_image = image;
|
||||
emit imageChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
bool ImageItem::isValid() const
|
||||
{
|
||||
return !_image.isNull();
|
||||
}
|
31
src/imageitem.h
Normal file
31
src/imageitem.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef IMAGEITEM_H
|
||||
#define IMAGEITEM_H
|
||||
|
||||
#include <QQuickPaintedItem>
|
||||
#include <QPainter>
|
||||
#include <QImage>
|
||||
|
||||
class ImageItem : public QQuickPaintedItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QImage image READ getImage WRITE setImage NOTIFY imageChanged)
|
||||
Q_PROPERTY(bool valid READ isValid NOTIFY imageChanged)
|
||||
public:
|
||||
ImageItem();
|
||||
|
||||
void paint(QPainter *painter);
|
||||
|
||||
signals:
|
||||
void imageChanged();
|
||||
|
||||
private:
|
||||
QImage getImage() const;
|
||||
void setImage(const QImage &image);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
QImage _image;
|
||||
};
|
||||
|
||||
#endif // IMAGEITEM_H
|
|
@ -1,7 +1,6 @@
|
|||
#include "ippdiscovery.h"
|
||||
#include "ippprinter.h"
|
||||
#include "settings.h"
|
||||
#include <seaprint_version.h>
|
||||
|
||||
#define A 1
|
||||
#define PTR 12
|
||||
|
@ -38,7 +37,7 @@ QStringList get_addr(Bytestream& bts)
|
|||
return addr;
|
||||
}
|
||||
|
||||
IppDiscovery::IppDiscovery() : QStringListModel(), QQuickImageProvider(QQuickImageProvider::Image, ForceAsynchronousImageLoading)
|
||||
IppDiscovery::IppDiscovery() : QStringListModel()
|
||||
{
|
||||
socket = new QUdpSocket(this);
|
||||
connect(socket, &QUdpSocket::readyRead, this, &IppDiscovery::readPendingDatagrams);
|
||||
|
@ -94,15 +93,15 @@ void IppDiscovery::sendQuery(quint16 qtype, QStringList addrs) {
|
|||
QTime now = QTime::currentTime();
|
||||
QTime aWhileAgo = now.addSecs(-1);
|
||||
|
||||
foreach(QString oq, _outstandingQueries.keys())
|
||||
for(QPair<quint16, QString> oq : _outstandingQueries.keys())
|
||||
{
|
||||
if(_outstandingQueries[oq] < aWhileAgo)
|
||||
{ // Housekeeping for _outstandingQueries
|
||||
_outstandingQueries.remove(oq);
|
||||
}
|
||||
else if(addrs.contains(oq))
|
||||
else if(oq.first == qtype && addrs.contains(oq.second))
|
||||
{ // we recently asked about this, remove it
|
||||
addrs.removeOne(oq);
|
||||
addrs.removeOne(oq.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,9 +121,9 @@ void IppDiscovery::sendQuery(quint16 qtype, QStringList addrs) {
|
|||
|
||||
query << _transactionid++ << flags << questions << (quint16)0 << (quint16)0 << (quint16)0;
|
||||
|
||||
foreach(QString addr, addrs)
|
||||
for(QString addr : addrs)
|
||||
{
|
||||
_outstandingQueries.insert(addr, now);
|
||||
_outstandingQueries.insert({qtype, addr}, now);
|
||||
|
||||
QStringList addrParts = addr.split(".");
|
||||
QString addrPart, restAddr;
|
||||
|
@ -154,9 +153,7 @@ void IppDiscovery::sendQuery(quint16 qtype, QStringList addrs) {
|
|||
|
||||
}
|
||||
|
||||
QByteArray bytes((char*)(query.raw()), query.size());
|
||||
socket->writeDatagram(bytes, QHostAddress("224.0.0.251"), 5353);
|
||||
|
||||
socket->writeDatagram((char*)(query.raw()), query.size(), QHostAddress("224.0.0.251"), 5353);
|
||||
}
|
||||
|
||||
QString make_addr(QString proto, int defaultPort, quint16 port, QString ip, QString rp)
|
||||
|
@ -188,11 +185,15 @@ void IppDiscovery::update()
|
|||
QList<QPair<QString,QString>> ippsIpRps;
|
||||
QString target, rp;
|
||||
|
||||
foreach(QString it, _ipps)
|
||||
for(QString it : _ipps)
|
||||
{
|
||||
quint16 port = _ports[it];
|
||||
target = _targets[it];
|
||||
rp = _rps[it];
|
||||
if(!_targets.contains(it) || !_ports.contains(it) || !_rps.contains(it))
|
||||
continue;
|
||||
|
||||
quint16 port = _ports.value(it);
|
||||
target = _targets.value(it);
|
||||
rp = _rps.value(it);
|
||||
|
||||
|
||||
for(QMultiMap<QString,QString>::Iterator ait = _AAs.begin(); ait != _AAs.end(); ait++)
|
||||
{
|
||||
|
@ -209,11 +210,14 @@ void IppDiscovery::update()
|
|||
}
|
||||
}
|
||||
|
||||
foreach(QString it, _ipp)
|
||||
for(QString it : _ipp)
|
||||
{
|
||||
quint16 port = _ports[it];
|
||||
target = _targets[it];
|
||||
rp = _rps[it];
|
||||
if(!_targets.contains(it) || !_ports.contains(it) || !_rps.contains(it))
|
||||
continue;
|
||||
|
||||
quint16 port = _ports.value(it);
|
||||
target = _targets.value(it);
|
||||
rp = _rps.value(it);
|
||||
|
||||
for(QMultiMap<QString,QString>::Iterator ait = _AAs.begin(); ait != _AAs.end(); ait++)
|
||||
{
|
||||
|
@ -235,9 +239,9 @@ void IppDiscovery::update()
|
|||
|
||||
// Counting on that _ipp duplicates doesn't resolve fully any erlier than their _ipps counterpart
|
||||
|
||||
// TODO?: replace this with some logica that can bpoth add and remove
|
||||
// TODO?: replace this with some logic that can bpoth add and remove
|
||||
// and it can consider _favourites, so we can drop cleanUpdate
|
||||
foreach(QString f, found)
|
||||
for(QString f : found)
|
||||
{
|
||||
if(!this->stringList().contains(f))
|
||||
{
|
||||
|
@ -253,16 +257,23 @@ void IppDiscovery::update()
|
|||
void IppDiscovery::updateAndQueryPtrs(QStringList& ptrs, QStringList new_ptrs)
|
||||
{
|
||||
new_ptrs.removeDuplicates();
|
||||
foreach(QString ptr, new_ptrs)
|
||||
for(QString ptr : new_ptrs)
|
||||
{
|
||||
if(!ptrs.contains(ptr))
|
||||
{
|
||||
ptrs.append(ptr);
|
||||
}
|
||||
// If pointer does not resolve to a target or is missing information, query about it
|
||||
if(!_targets.contains(ptr) || !_ports.contains(ptr) || !_rps.contains(ptr))
|
||||
{ // if the PTR doesn't already resolve, ask for everything about it
|
||||
sendQuery(ALL, {ptr});
|
||||
if(!_rps.contains(ptr))
|
||||
{
|
||||
// Avahi *really* hates sending TXT to anything else than a TXT query
|
||||
qDebug() << "querying txt " << ptr;
|
||||
sendQuery(TXT, {ptr});
|
||||
}
|
||||
if(!_targets.contains(ptr) || !_ports.contains(ptr))
|
||||
{
|
||||
qDebug() << "querying srv " << ptr;
|
||||
sendQuery(SRV, {ptr});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -329,6 +340,7 @@ void IppDiscovery::readPendingDatagrams()
|
|||
{
|
||||
std::string tmprp;
|
||||
tmp/tmp.remaining() >> tmprp;
|
||||
qDebug() << "tmprp" << aaddr << tmprp.c_str();
|
||||
_rps[aaddr] = tmprp.c_str();
|
||||
}
|
||||
}
|
||||
|
@ -371,7 +383,7 @@ void IppDiscovery::readPendingDatagrams()
|
|||
qDebug() << "new ipp ptrs" << new_ipp_ptrs;
|
||||
qDebug() << "new ipps ptrs" << new_ipps_ptrs;
|
||||
qDebug() << "ipp ptrs" << _ipp;
|
||||
qDebug() << "ipp ptrs" << _ipps;
|
||||
qDebug() << "ipps ptrs" << _ipps;
|
||||
qDebug() << "rps" << _rps;
|
||||
qDebug() << "ports" << _ports;
|
||||
qDebug() << "new targets" << new_targets;
|
||||
|
@ -381,12 +393,12 @@ void IppDiscovery::readPendingDatagrams()
|
|||
|
||||
// These will send one query per unique new ptr.
|
||||
// some responders doesn't give TXT records for more than one thing at at time :(
|
||||
updateAndQueryPtrs(_ipp, new_ipp_ptrs);
|
||||
updateAndQueryPtrs(_ipps, new_ipps_ptrs);
|
||||
updateAndQueryPtrs(_ipp, new_ipp_ptrs);
|
||||
|
||||
QStringList unresolvedAddrs;
|
||||
|
||||
foreach(QString t, new_targets)
|
||||
for(QString t : new_targets)
|
||||
{
|
||||
// If target does not resolve to an address, query about it
|
||||
if(!_AAs.contains(t))
|
||||
|
@ -405,15 +417,8 @@ void IppDiscovery::readPendingDatagrams()
|
|||
|
||||
}
|
||||
|
||||
QImage IppDiscovery::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
|
||||
{ //TODO: consider caching images (doesn't appear to be needed currently)
|
||||
Q_UNUSED(requestedSize);
|
||||
qDebug() << "requesting image" << id;
|
||||
|
||||
QImage img;
|
||||
|
||||
QNetworkAccessManager* nam = new QNetworkAccessManager();
|
||||
QUrl url(id);
|
||||
bool IppDiscovery::resolve(QUrl& url)
|
||||
{
|
||||
QString host = url.host();
|
||||
|
||||
if(host.endsWith("."))
|
||||
|
@ -421,31 +426,11 @@ QImage IppDiscovery::requestImage(const QString &id, QSize *size, const QSize &r
|
|||
host.chop(1);
|
||||
}
|
||||
|
||||
qDebug() << url.host() << host << _AAs;
|
||||
// TODO IPv6
|
||||
if(_AAs.contains(host))
|
||||
{ // TODO: retry potential other IPs
|
||||
url.setHost(_AAs.value(host));
|
||||
return true;
|
||||
}
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, "SeaPrint " SEAPRINT_VERSION);
|
||||
connect(nam, &QNetworkAccessManager::sslErrors, &IppPrinter::ignoreSslErrors);
|
||||
|
||||
QNetworkReply* reply = nam->get(request);
|
||||
|
||||
QEventLoop loop;
|
||||
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
QImageReader imageReader(reply);
|
||||
img = imageReader.read();
|
||||
}
|
||||
|
||||
*size = img.size();
|
||||
delete nam;
|
||||
return img;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
#ifndef IPPDISCOVERY_H
|
||||
#define IPPDISCOVERY_H
|
||||
#include <QStringListModel>
|
||||
#include <QQuickImageProvider>
|
||||
#include <QUdpSocket>
|
||||
#include <QMutex>
|
||||
#include <QImageReader>
|
||||
#include <QtNetwork>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QEventLoop>
|
||||
#include "bytestream.h"
|
||||
|
||||
class IppDiscovery : public QStringListModel, public QQuickImageProvider
|
||||
class IppDiscovery : public QStringListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -21,6 +16,8 @@ public:
|
|||
Q_INVOKABLE void discover();
|
||||
Q_INVOKABLE void reset();
|
||||
|
||||
bool resolve(QUrl& url);
|
||||
|
||||
signals:
|
||||
void favouritesChanged();
|
||||
void ignoreSslErrorsChanged();
|
||||
|
@ -47,9 +44,6 @@ private:
|
|||
|
||||
void updateAndQueryPtrs(QStringList& ptrs, QStringList new_ptrs);
|
||||
|
||||
|
||||
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
|
||||
|
||||
QStringList _ipp;
|
||||
QStringList _ipps;
|
||||
QMap<QString,QString> _rps;
|
||||
|
@ -59,7 +53,7 @@ private:
|
|||
QMultiMap<QString,QString> _AAs;
|
||||
QMultiMap<QString,QString> _AAAAs;
|
||||
|
||||
QMap<QString, QTime> _outstandingQueries;
|
||||
QMap<QPair<quint16, QString>, QTime> _outstandingQueries;
|
||||
|
||||
QStringList _favourites;
|
||||
QUdpSocket* socket;
|
||||
|
|
152
src/ippmsg.cpp
152
src/ippmsg.cpp
|
@ -6,8 +6,9 @@ IppMsg::IppMsg()
|
|||
{
|
||||
}
|
||||
|
||||
IppMsg::IppMsg(QJsonObject opAttrs, QJsonObject jobAttrs, quint8 majVsn, quint8 minVsn)
|
||||
IppMsg::IppMsg(Operation operation, QJsonObject opAttrs, QJsonObject jobAttrs, quint8 majVsn, quint8 minVsn)
|
||||
{
|
||||
_operation = operation;
|
||||
_majVsn = majVsn;
|
||||
_minVsn = minVsn;
|
||||
_opAttrs = opAttrs;
|
||||
|
@ -19,52 +20,10 @@ IppMsg::~IppMsg()
|
|||
{
|
||||
}
|
||||
|
||||
IppMsg::IppMsg(QNetworkReply* resp)
|
||||
|
||||
IppMsg::IppMsg(Bytestream& bts)
|
||||
{
|
||||
QByteArray tmp = resp->readAll();
|
||||
Bytestream bts(tmp.constData(), tmp.length());
|
||||
|
||||
quint32 reqId;
|
||||
|
||||
bts >> _majVsn >> _minVsn >> _status >> reqId;
|
||||
|
||||
QJsonObject attrs;
|
||||
IppMsg::IppTag currentAttrType = IppTag::EndAttrs;
|
||||
|
||||
while(!bts.atEnd())
|
||||
{
|
||||
if(bts.peekU8() <= IppTag::UnsupportedAttrs) {
|
||||
|
||||
if(currentAttrType == IppTag::OpAttrs) {
|
||||
_opAttrs = attrs;
|
||||
}
|
||||
else if (currentAttrType == IppTag::JobAttrs) {
|
||||
_jobAttrs.append(attrs);
|
||||
}
|
||||
else if (currentAttrType == IppTag::PrinterAttrs) {
|
||||
_printerAttrs = attrs;
|
||||
}
|
||||
else if (currentAttrType == IppTag::UnsupportedAttrs) {
|
||||
qDebug() << "WARNING: unsupported attrs reported:" << attrs;
|
||||
}
|
||||
|
||||
if(bts >>= (uint8_t)IppTag::EndAttrs) {
|
||||
break;
|
||||
}
|
||||
|
||||
currentAttrType = (IppTag)bts.getU8();
|
||||
attrs = QJsonObject();
|
||||
|
||||
}
|
||||
else {
|
||||
consume_attribute(attrs, bts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IppMsg::IppMsg(QByteArray resp)
|
||||
{
|
||||
Bytestream bts(resp.constData(), resp.length());
|
||||
// Bytestream bts(resp.constData(), resp.length());
|
||||
|
||||
quint32 reqId;
|
||||
|
||||
|
@ -118,40 +77,63 @@ QJsonValue IppMsg::consume_value(quint8 tag, Bytestream& data)
|
|||
Q_ASSERT(false);
|
||||
case Integer:
|
||||
case Enum:
|
||||
quint32 tmp_u32;
|
||||
data >> tmp_len >> tmp_u32;
|
||||
{
|
||||
quint32 tmp_u32=0;
|
||||
if(!(data >>= (quint16)0))
|
||||
{
|
||||
data >> (quint16)4 >> tmp_u32;
|
||||
}
|
||||
value = (int)tmp_u32;
|
||||
break;
|
||||
}
|
||||
case Boolean:
|
||||
quint8 tmp_bool;
|
||||
data >> tmp_len >> tmp_bool;
|
||||
{
|
||||
quint8 tmp_bool=0;
|
||||
if(!(data >>= (quint16)0))
|
||||
{
|
||||
data >> (quint16)1 >> tmp_bool;
|
||||
}
|
||||
value = (bool)tmp_bool;
|
||||
break;
|
||||
}
|
||||
case DateTime:
|
||||
{
|
||||
QDateTime tmp_datetime;
|
||||
if(!(data >>= (quint16)0))
|
||||
{
|
||||
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
|
||||
data >> (quint16)11 >> 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);
|
||||
tmp_datetime = QDateTime(date, time, Qt::OffsetFromUTC, offset_seconds);
|
||||
}
|
||||
value = tmp_datetime.toString(Qt::ISODate);
|
||||
break;
|
||||
}
|
||||
case Resolution:
|
||||
{
|
||||
qint32 x, y;
|
||||
qint8 units;
|
||||
data >> tmp_len >> x >> y >> units;
|
||||
qint32 x=0;
|
||||
qint32 y=0;
|
||||
qint8 units=0;
|
||||
if(!(data >>= (quint16)0))
|
||||
{
|
||||
data >> (quint16)9 >> x >> y >> units;
|
||||
}
|
||||
value = QJsonObject {{"x", x}, {"y", y}, {"units", units}};
|
||||
break;
|
||||
}
|
||||
case IntegerRange:
|
||||
{
|
||||
qint32 low, high;
|
||||
data >> tmp_len >> low >> high;
|
||||
qint32 low=0;
|
||||
qint32 high=0;
|
||||
if(!(data >>= (quint16)0))
|
||||
{
|
||||
data >> (quint16)8 >> low >> high;
|
||||
}
|
||||
value = QJsonObject {{"low", low}, {"high", high}};
|
||||
break;
|
||||
}
|
||||
|
@ -201,7 +183,7 @@ QJsonArray IppMsg::get_unnamed_attributes(Bytestream& data)
|
|||
QJsonArray untag_values(QJsonArray taggedValues)
|
||||
{
|
||||
QJsonArray res;
|
||||
foreach(QJsonValue it, taggedValues)
|
||||
for(QJsonValue it : taggedValues)
|
||||
{
|
||||
res.append(it.toObject()["value"]);
|
||||
}
|
||||
|
@ -293,17 +275,11 @@ QString IppMsg::consume_attribute(QJsonObject& attrs, Bytestream& data)
|
|||
name = tmp_str.c_str();
|
||||
|
||||
taggedValue = consume_value(tag, data);
|
||||
|
||||
QJsonArray unnamed = get_unnamed_attributes(data);
|
||||
|
||||
// qDebug() << name0 << tag << tmp_len << value << unnamed;
|
||||
|
||||
if(tag == BeginCollection)
|
||||
{
|
||||
// qDebug() << "Unnamed attrs for collection" << unnamed;
|
||||
|
||||
QJsonValue collected = collect_attributes(unnamed);
|
||||
// qDebug() << "collected" << collected;
|
||||
taggedValue = QJsonObject {{"tag", tag}, {"value", collected}};
|
||||
}
|
||||
|
||||
|
@ -312,6 +288,7 @@ QString IppMsg::consume_attribute(QJsonObject& attrs, Bytestream& data)
|
|||
|| name.startsWith("printer-firmware")
|
||||
|| name.endsWith("-supported")
|
||||
|| name == "printer-icons"
|
||||
|| name == "media-ready"
|
||||
|| name.endsWith("-reasons")));
|
||||
|
||||
if(!unnamed.empty() || forceArray)
|
||||
|
@ -327,13 +304,13 @@ QString IppMsg::consume_attribute(QJsonObject& attrs, Bytestream& data)
|
|||
return name;
|
||||
}
|
||||
|
||||
QByteArray IppMsg::encode(Operation op)
|
||||
Bytestream IppMsg::encode()
|
||||
{
|
||||
Bytestream ipp;
|
||||
|
||||
ipp << _majVsn << _minVsn;
|
||||
|
||||
ipp << quint16(op);
|
||||
ipp << quint16(_operation);
|
||||
ipp << _reqid++;
|
||||
|
||||
|
||||
|
@ -343,12 +320,16 @@ QByteArray IppMsg::encode(Operation op)
|
|||
QStringList InitialAttrs = {"attributes-charset",
|
||||
"attributes-natural-language",
|
||||
"printer-uri",
|
||||
"job-id",
|
||||
"requesting-user-name"};
|
||||
foreach(QString key, InitialAttrs)
|
||||
for(QString key : InitialAttrs)
|
||||
{
|
||||
if(_opAttrs.find(key) != _opAttrs.end())
|
||||
{
|
||||
QJsonObject val = _opAttrs.take(key).toObject();
|
||||
encode_attr(ipp, val["tag"].toInt(), key, val["value"]);
|
||||
}
|
||||
}
|
||||
for(QJsonObject::iterator it = _opAttrs.begin(); it != _opAttrs.end(); it++)
|
||||
{ // encode any remaining op-attrs
|
||||
QJsonObject val = it.value().toObject();
|
||||
|
@ -369,7 +350,7 @@ QByteArray IppMsg::encode(Operation op)
|
|||
|
||||
ipp << quint8(EndAttrs);
|
||||
|
||||
return QByteArray((char*)(ipp.raw()), ipp.size());
|
||||
return ipp;
|
||||
}
|
||||
|
||||
void IppMsg::encode_attr(Bytestream& msg, quint8 tag, QString name, QJsonValue value, bool inCollection)
|
||||
|
@ -381,7 +362,25 @@ void IppMsg::encode_attr(Bytestream& msg, quint8 tag, QString name, QJsonValue v
|
|||
name = "";
|
||||
}
|
||||
msg << tag << quint16(name.length()) << name.toStdString();
|
||||
if(value.isArray())
|
||||
{
|
||||
QJsonArray array = value.toArray();
|
||||
value = array.takeAt(0);
|
||||
encode_value(msg, tag, value);
|
||||
for(QJsonValue v : array)
|
||||
{
|
||||
msg << tag << quint16(0);
|
||||
encode_value(msg, tag, v);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
encode_value(msg, tag, value);
|
||||
}
|
||||
}
|
||||
|
||||
void IppMsg::encode_value(Bytestream& msg, quint8 tag, QJsonValue value)
|
||||
{
|
||||
|
||||
switch (tag) {
|
||||
case OpAttrs:
|
||||
|
@ -398,7 +397,7 @@ void IppMsg::encode_attr(Bytestream& msg, quint8 tag, QString name, QJsonValue v
|
|||
}
|
||||
case Boolean:
|
||||
{
|
||||
quint32 tmp_u8 = value.toBool();
|
||||
quint8 tmp_u8 = value.toBool();
|
||||
msg << (quint16)1 << tmp_u8;
|
||||
break;
|
||||
}
|
||||
|
@ -425,20 +424,12 @@ void IppMsg::encode_attr(Bytestream& msg, quint8 tag, QString name, QJsonValue v
|
|||
case BeginCollection:
|
||||
{
|
||||
msg << (quint16)0; // length of value
|
||||
if(value.isObject())
|
||||
{
|
||||
QJsonObject collection = value.toObject();
|
||||
foreach(QString key, collection.keys())
|
||||
for(QString key : collection.keys())
|
||||
{
|
||||
encode_attr(msg, collection[key].toObject()["tag"].toInt(), key,
|
||||
collection[key].toObject()["value"], true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO add support for 1-setOf in collections
|
||||
Q_ASSERT("FIXME-array");
|
||||
}
|
||||
msg << (quint8)EndCollection << (quint16)0 << (quint16)0;
|
||||
break;
|
||||
}
|
||||
|
@ -465,3 +456,8 @@ void IppMsg::encode_attr(Bytestream& msg, quint8 tag, QString name, QJsonValue v
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IppMsg::setOpAttr(QString name, IppMsg::IppTag type, QJsonValue value)
|
||||
{
|
||||
_opAttrs.insert(name, QJsonObject {{"tag", type}, {"value", value}});
|
||||
}
|
||||
|
|
30
src/ippmsg.h
30
src/ippmsg.h
|
@ -1,15 +1,14 @@
|
|||
#ifndef IPP_PROTO_H
|
||||
#define IPP_PROTO_H
|
||||
#ifndef IPP_MSG_H
|
||||
#define IPP_MSG_H
|
||||
|
||||
#include "bytestream.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QDate>
|
||||
#include <QTime>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QtNetwork>
|
||||
#include <QNetworkReply>
|
||||
#include <QIODevice>
|
||||
|
||||
class IppMsg
|
||||
{
|
||||
|
@ -62,21 +61,23 @@ public:
|
|||
RestartJob = 0x000E,
|
||||
PausePrinter = 0x0010,
|
||||
ResumePrinter = 0x0011,
|
||||
PurgeJobs = 0x0012
|
||||
PurgeJobs = 0x0012,
|
||||
IdentifyPrinter = 0x003C
|
||||
};
|
||||
|
||||
explicit IppMsg();
|
||||
explicit IppMsg(QNetworkReply* resp);
|
||||
explicit IppMsg(QByteArray resp);
|
||||
IppMsg(QJsonObject opAttrs, QJsonObject jobAttrs=QJsonObject(), quint8 majVsn=1, quint8 minVsn=1);
|
||||
explicit IppMsg(Bytestream& resp);
|
||||
IppMsg(Operation operation, QJsonObject opAttrs, QJsonObject jobAttrs=QJsonObject(), quint8 majVsn=1, quint8 minVsn=1);
|
||||
IppMsg(const IppMsg& other) = default;
|
||||
~IppMsg();
|
||||
|
||||
QByteArray encode(Operation op);
|
||||
Bytestream encode();
|
||||
QJsonObject getPrinterAttrs() {return _printerAttrs;}
|
||||
QJsonArray getJobAttrs() {return _jobAttrs;}
|
||||
QJsonObject getOpAttrs() {return _opAttrs;}
|
||||
|
||||
void setOpAttr(QString name, IppMsg::IppTag type, QJsonValue value);
|
||||
|
||||
quint16 getStatus() {return _status;}
|
||||
|
||||
|
||||
|
@ -87,6 +88,9 @@ private:
|
|||
QJsonValue collect_attributes(QJsonArray& attrs);
|
||||
QString consume_attribute(QJsonObject& attrs, Bytestream& data);
|
||||
void encode_attr(Bytestream& msg, quint8 tag, QString name, QJsonValue value, bool subCollection=false);
|
||||
void encode_value(Bytestream& msg, quint8 tag, QJsonValue value);
|
||||
|
||||
Operation _operation;
|
||||
|
||||
quint8 _majVsn;
|
||||
quint8 _minVsn;
|
||||
|
@ -100,4 +104,4 @@ private:
|
|||
static quint32 _reqid;
|
||||
};
|
||||
|
||||
#endif // IPP_PROTO_H
|
||||
#endif // IPP_MSG_H
|
||||
|
|
|
@ -1,43 +1,41 @@
|
|||
#include "ippprinter.h"
|
||||
#include <seaprint_version.h>
|
||||
#include "ippdiscovery.h"
|
||||
#include "mimer.h"
|
||||
#include "convertchecker.h"
|
||||
#include "papersizes.h"
|
||||
#include "overrider.h"
|
||||
#include "settings.h"
|
||||
#include <fstream>
|
||||
|
||||
Q_DECLARE_METATYPE(QMargins)
|
||||
Q_DECLARE_METATYPE(IppMsg)
|
||||
|
||||
IppPrinter::IppPrinter()
|
||||
{
|
||||
_worker = new PrinterWorker(this);
|
||||
|
||||
QObject::connect(this, &IppPrinter::urlChanged, this, &IppPrinter::onUrlChanged);
|
||||
qRegisterMetaType<QTemporaryFile*>("QTemporaryFile*");
|
||||
|
||||
_worker = new ConvertWorker(this);
|
||||
_worker->moveToThread(&_workerThread);
|
||||
connect(this, &IppPrinter::doDoGetPrinterAttributes, _worker, &PrinterWorker::getPrinterAttributes);
|
||||
connect(this, &IppPrinter::doGetJobs, _worker, &PrinterWorker::getJobs);
|
||||
connect(this, &IppPrinter::doCancelJob, _worker, &PrinterWorker::cancelJob);
|
||||
connect(this, &IppPrinter::doIdentify, _worker, &PrinterWorker::identify);
|
||||
connect(this, &IppPrinter::doPrint, _worker, &PrinterWorker::print);
|
||||
connect(this, &IppPrinter::doPrint2, _worker, &PrinterWorker::print2);
|
||||
|
||||
connect(&_workerThread, &QThread::finished, _worker, &QObject::deleteLater);
|
||||
connect(this, &IppPrinter::doGetStrings, _worker, &PrinterWorker::getStrings);
|
||||
connect(this, &IppPrinter::doGetImage, _worker, &PrinterWorker::getImage);
|
||||
|
||||
connect(this, &IppPrinter::doCommand, _worker, &ConvertWorker::command);
|
||||
connect(this, &IppPrinter::doGetJobs, _worker, &ConvertWorker::getJobs);
|
||||
connect(this, &IppPrinter::doCancelJob, _worker, &ConvertWorker::cancelJob);
|
||||
connect(this, &IppPrinter::doJustUpload, _worker, &ConvertWorker::justUpload);
|
||||
|
||||
connect(this, &IppPrinter::doConvertPdf, _worker, &ConvertWorker::convertPdf);
|
||||
connect(this, &IppPrinter::doConvertImage, _worker, &ConvertWorker::convertImage);
|
||||
connect(this, &IppPrinter::doConvertOfficeDocument, _worker, &ConvertWorker::convertOfficeDocument);
|
||||
connect(this, &IppPrinter::doConvertPlaintext, _worker, &ConvertWorker::convertPlaintext);
|
||||
connect(_worker, &ConvertWorker::progress, this, &IppPrinter::setProgress);
|
||||
connect(_worker, &ConvertWorker::busyMessage, this, &IppPrinter::setBusyMessage);
|
||||
connect(_worker, &ConvertWorker::failed, this, &IppPrinter::convertFailed);
|
||||
connect(_worker, &PrinterWorker::progress, this, &IppPrinter::setProgress);
|
||||
connect(_worker, &PrinterWorker::busyMessage, this, &IppPrinter::setBusyMessage);
|
||||
connect(_worker, &PrinterWorker::failed, this, &IppPrinter::convertFailed);
|
||||
|
||||
qRegisterMetaType<QMargins>();
|
||||
|
||||
_workerThread.start();
|
||||
qRegisterMetaType<IppMsg>();
|
||||
}
|
||||
|
||||
IppPrinter::~IppPrinter() {
|
||||
// TODO: delete worker and workerthread?
|
||||
|
||||
}
|
||||
|
||||
QJsonObject IppPrinter::opAttrs() {
|
||||
|
@ -56,8 +54,6 @@ void IppPrinter::setUrl(QString url_s)
|
|||
{
|
||||
QUrl url = QUrl(url_s);
|
||||
|
||||
qDebug() << url.scheme();
|
||||
|
||||
// If not already a good scheme, try to fixup, or give an empty url
|
||||
if(url.scheme() != "ipp" && url.scheme() != "ipps" && url.scheme() != "file")
|
||||
{
|
||||
|
@ -87,44 +83,110 @@ void IppPrinter::setUrl(QString url_s)
|
|||
|
||||
void IppPrinter::onUrlChanged()
|
||||
{
|
||||
QMetaObject::invokeMethod(_worker, "urlChanged", Q_ARG(QUrl, httpUrl()));
|
||||
refresh();
|
||||
}
|
||||
|
||||
void IppPrinter::refresh() {
|
||||
// _attrs = QJsonObject();
|
||||
// emit attrsChanged();
|
||||
|
||||
// _additionalDocumentFormats = QStringList();
|
||||
// emit additionalDocumentFormatsChanged();
|
||||
|
||||
|
||||
if(_url.scheme() == "file")
|
||||
{
|
||||
_attrs = QJsonObject();
|
||||
QString localFile = _url.toLocalFile();
|
||||
|
||||
QFile file(_url.toLocalFile());
|
||||
if(localFile.endsWith(".raw") || localFile.endsWith(".bin"))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ifstream ifs = std::ifstream(localFile.toStdString(), std::ios::in | std::ios::binary);
|
||||
Bytestream InBts(ifs);
|
||||
IppMsg resp(InBts);
|
||||
qDebug() << resp.getStatus() << resp.getOpAttrs() << resp.getPrinterAttrs();
|
||||
_attrs = resp.getPrinterAttrs();
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
qDebug() << e.what();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QFile file(localFile);
|
||||
if(file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QJsonDocument JsonDocument = QJsonDocument::fromJson(file.readAll());
|
||||
|
||||
_attrs = JsonDocument.object();
|
||||
// These won't load anyway...r
|
||||
_attrs.remove("printer-icons");
|
||||
file.close();
|
||||
Overrider::instance()->apply(_attrs);
|
||||
}
|
||||
}
|
||||
Overrider::instance()->apply(_attrs);
|
||||
emit attrsChanged();
|
||||
|
||||
// For testing fake file-prinetrs with a strings file hosted elsewhere
|
||||
// MaybeGetIcon();
|
||||
// MaybeGetStrings();
|
||||
|
||||
UpdateAdditionalDocumentFormats();
|
||||
}
|
||||
else
|
||||
{
|
||||
QJsonObject o = opAttrs();
|
||||
|
||||
IppMsg msg = IppMsg(o);
|
||||
emit doCommand(msg.encode(IppMsg::GetPrinterAttrs));
|
||||
IppMsg msg = IppMsg(IppMsg::GetPrinterAttrs, o);
|
||||
emit doDoGetPrinterAttributes(msg.encode());
|
||||
}
|
||||
}
|
||||
|
||||
void IppPrinter::MaybeGetStrings()
|
||||
{
|
||||
if(_attrs.contains("printer-strings-uri") && _strings.empty())
|
||||
{
|
||||
QUrl url(_attrs["printer-strings-uri"].toObject()["value"].toString());
|
||||
resolveUrl(url);
|
||||
if(isAllowedAddress(url))
|
||||
{
|
||||
emit doGetStrings(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IppPrinter::MaybeGetIcon(bool retry)
|
||||
{
|
||||
if(_attrs.contains("printer-icons") && (_icon.isNull() || retry) && !_iconRetried)
|
||||
{
|
||||
QUrl url;
|
||||
QJsonArray icons = _attrs["printer-icons"].toObject()["value"].toArray();
|
||||
|
||||
if(retry)
|
||||
{ // If there were more than one icon, try the last one on the retry
|
||||
if(icons.size() > 1)
|
||||
{
|
||||
url = icons.last().toString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(icons.size() == 3)
|
||||
{ // If there are 3 icons, the first will be the 48px one, ignore it
|
||||
url = icons.at(1).toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
url = icons.at(0).toString();
|
||||
}
|
||||
}
|
||||
|
||||
resolveUrl(url);
|
||||
|
||||
if(isAllowedAddress(url))
|
||||
{
|
||||
emit doGetImage(url);
|
||||
}
|
||||
}
|
||||
_iconRetried = retry;
|
||||
}
|
||||
|
||||
|
||||
void IppPrinter::UpdateAdditionalDocumentFormats()
|
||||
{
|
||||
_additionalDocumentFormats = QStringList();
|
||||
|
@ -154,7 +216,7 @@ void IppPrinter::UpdateAdditionalDocumentFormats()
|
|||
emit additionalDocumentFormatsChanged();
|
||||
}
|
||||
|
||||
void IppPrinter::getPrinterAttributesFinished(CURLcode res, QByteArray data)
|
||||
void IppPrinter::getPrinterAttributesFinished(CURLcode res, Bytestream data)
|
||||
{
|
||||
qDebug() << res;
|
||||
_attrs = QJsonObject();
|
||||
|
@ -176,10 +238,12 @@ void IppPrinter::getPrinterAttributesFinished(CURLcode res, QByteArray data)
|
|||
|
||||
emit attrsChanged();
|
||||
|
||||
MaybeGetStrings();
|
||||
MaybeGetIcon();
|
||||
UpdateAdditionalDocumentFormats();
|
||||
}
|
||||
|
||||
void IppPrinter::printRequestFinished(CURLcode res, QByteArray data)
|
||||
void IppPrinter::printRequestFinished(CURLcode res, Bytestream data)
|
||||
{
|
||||
_jobAttrs = QJsonObject();
|
||||
bool status = false;
|
||||
|
@ -211,7 +275,7 @@ void IppPrinter::printRequestFinished(CURLcode res, QByteArray data)
|
|||
emit jobFinished(status);
|
||||
}
|
||||
|
||||
void IppPrinter::getJobsRequestFinished(CURLcode res, QByteArray data)
|
||||
void IppPrinter::getJobsRequestFinished(CURLcode res, Bytestream data)
|
||||
{
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
|
@ -229,7 +293,7 @@ void IppPrinter::getJobsRequestFinished(CURLcode res, QByteArray data)
|
|||
}
|
||||
|
||||
|
||||
void IppPrinter::cancelJobFinished(CURLcode res, QByteArray data)
|
||||
void IppPrinter::cancelJobFinished(CURLcode res, Bytestream data)
|
||||
{
|
||||
bool status = false;
|
||||
if(res == CURLE_OK)
|
||||
|
@ -248,14 +312,48 @@ void IppPrinter::cancelJobFinished(CURLcode res, QByteArray data)
|
|||
getJobs();
|
||||
}
|
||||
|
||||
|
||||
void IppPrinter::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
||||
void IppPrinter::identifyFinished(CURLcode /*res*/, Bytestream /*data*/)
|
||||
{
|
||||
bool ignore = Settings::instance()->ignoreSslErrors();
|
||||
qDebug() << reply->request().url() << "SSL handshake failed" << errors << ignore;
|
||||
if(ignore)
|
||||
|
||||
}
|
||||
|
||||
void IppPrinter::getStringsFinished(CURLcode res, Bytestream data)
|
||||
{
|
||||
qDebug() << res << data.size();
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
reply->ignoreSslErrors(errors);
|
||||
QByteArray ba((char*)data.raw(), data.size());
|
||||
// "media-type.com.epson-coated" = "Epson Photo Quality Ink Jet";
|
||||
QRegularExpression re("^\\\"(.*)\\\"\\s*=\\s*\\\"(.*)\\\";");
|
||||
QList<QByteArray> bl = ba.split('\n');
|
||||
for(QByteArray l : bl)
|
||||
{
|
||||
QRegularExpressionMatch match = re.match(l);
|
||||
if(match.hasMatch())
|
||||
{
|
||||
_strings[match.captured(1)] = match.captured(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IppPrinter::getImageFinished(CURLcode res, Bytestream data)
|
||||
{
|
||||
qDebug() << res << data.size();
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
QImage tmp;
|
||||
if(tmp.loadFromData(data.raw(), data.size(), "PNG"))
|
||||
{
|
||||
_icon = tmp;
|
||||
qDebug() << "image loaded" << _icon;
|
||||
emit iconChanged();
|
||||
|
||||
if(tmp.size().width() < 128)
|
||||
{
|
||||
MaybeGetIcon(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,17 +381,27 @@ QString targetFormatIfAuto(QString documentFormat, QString mimeType, QJsonArray
|
|||
{
|
||||
if(documentFormat == Mimer::OctetStream)
|
||||
{
|
||||
if(mimeType == Mimer::PDF || mimeType == Mimer::Plaintext)
|
||||
QStringList PdfPrioList = {Mimer::PDF, Mimer::Postscript, Mimer::PWG, Mimer::URF};
|
||||
if(mimeType == Mimer::PDF)
|
||||
{
|
||||
return firstMatch(supportedMimeTypes, {Mimer::PDF, Mimer::Postscript, Mimer::PWG, Mimer::URF });
|
||||
return firstMatch(supportedMimeTypes, PdfPrioList);
|
||||
}
|
||||
else if(mimeType == Mimer::Postscript)
|
||||
{
|
||||
return firstMatch(supportedMimeTypes, {Mimer::Postscript});
|
||||
}
|
||||
else if(mimeType == Mimer::Plaintext)
|
||||
{
|
||||
return firstMatch(supportedMimeTypes, PdfPrioList << Mimer::Plaintext);
|
||||
}
|
||||
else if(Mimer::isOffice(mimeType))
|
||||
{
|
||||
return firstMatch(supportedMimeTypes, {Mimer::PDF, Mimer::Postscript, Mimer::PWG, Mimer::URF });
|
||||
return firstMatch(supportedMimeTypes, PdfPrioList);
|
||||
}
|
||||
else if(documentFormat == Mimer::SVG)
|
||||
{
|
||||
QStringList SvgPrioList {Mimer::PWG, Mimer::URF, Mimer::PDF, Mimer::Postscript};
|
||||
return firstMatch(supportedMimeTypes, SvgPrioList);
|
||||
}
|
||||
else if(Mimer::isImage(mimeType))
|
||||
{
|
||||
|
@ -311,29 +419,28 @@ QString targetFormatIfAuto(QString documentFormat, QString mimeType, QJsonArray
|
|||
return documentFormat;
|
||||
}
|
||||
|
||||
void IppPrinter::adjustRasterSettings(QString documentFormat, QJsonObject& jobAttrs, quint32& HwResX, quint32& HwResY,
|
||||
bool& BackHFlip, bool& BackVFlip)
|
||||
void IppPrinter::adjustRasterSettings(QString filename, QString mimeType, QJsonObject& jobAttrs, PrintParameters& Params)
|
||||
{
|
||||
if(documentFormat != Mimer::PWG && documentFormat != Mimer::URF)
|
||||
if(Params.format != PrintParameters::PWG && Params.format != PrintParameters::URF)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO? jobAttrs.remove("printer-resolution");
|
||||
jobAttrs.remove("printer-resolution");
|
||||
|
||||
if(documentFormat == Mimer::PWG)
|
||||
if(Params.format == PrintParameters::PWG)
|
||||
{
|
||||
quint32 diff = std::numeric_limits<quint32>::max();
|
||||
quint32 AdjustedHwResX = HwResX;
|
||||
quint32 AdjustedHwResY = HwResY;
|
||||
foreach(QJsonValue res, _attrs["pwg-raster-document-resolution-supported"].toObject()["value"].toArray())
|
||||
quint32 AdjustedHwResX = Params.hwResW;
|
||||
quint32 AdjustedHwResY = Params.hwResH;
|
||||
for(QJsonValue res : _attrs["pwg-raster-document-resolution-supported"].toObject()["value"].toArray())
|
||||
{
|
||||
QJsonObject resObj = res.toObject();
|
||||
if(resObj["units"] != 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
quint32 tmpDiff = std::abs(int(HwResX-resObj["x"].toInt())) + std::abs(int(HwResY-resObj["y"].toInt()));
|
||||
quint32 tmpDiff = std::abs(int(Params.hwResW-resObj["x"].toInt())) + std::abs(int(Params.hwResH-resObj["y"].toInt()));
|
||||
if(tmpDiff < diff)
|
||||
{
|
||||
diff = tmpDiff;
|
||||
|
@ -341,27 +448,26 @@ void IppPrinter::adjustRasterSettings(QString documentFormat, QJsonObject& jobAt
|
|||
AdjustedHwResY = resObj["y"].toInt();
|
||||
}
|
||||
}
|
||||
HwResX = AdjustedHwResX;
|
||||
HwResY = AdjustedHwResY;
|
||||
Params.hwResW = AdjustedHwResX;
|
||||
Params.hwResH = AdjustedHwResY;
|
||||
}
|
||||
|
||||
if(documentFormat == Mimer::URF)
|
||||
{ // Ensure symmetric resolution for URF
|
||||
HwResX = HwResY = std::min(HwResX, HwResY);
|
||||
else if(Params.format == PrintParameters::URF)
|
||||
{ // Ensure Params.format resolution for URF
|
||||
Params.hwResW = Params.hwResH = std::min(Params.hwResW, Params.hwResH);
|
||||
|
||||
quint32 diff = std::numeric_limits<quint32>::max();
|
||||
quint32 AdjustedHwRes = HwResX;
|
||||
quint32 AdjustedHwRes = Params.hwResW;
|
||||
|
||||
QJsonArray urfSupported = _attrs["urf-supported"].toObject()["value"].toArray();
|
||||
foreach(QJsonValue us, urfSupported)
|
||||
for(QJsonValue us : urfSupported)
|
||||
{
|
||||
if(us.toString().startsWith("RS"))
|
||||
{ //RS300[-600]
|
||||
QStringList resolutions = us.toString().mid(2).split("-");
|
||||
foreach(QString res, resolutions)
|
||||
for(QString res : resolutions)
|
||||
{
|
||||
int intRes = res.toInt();
|
||||
quint32 tmpDiff = std::abs(int(HwResX - intRes));
|
||||
quint32 tmpDiff = std::abs(int(Params.hwResW - intRes));
|
||||
if(tmpDiff < diff)
|
||||
{
|
||||
diff = tmpDiff;
|
||||
|
@ -369,7 +475,7 @@ void IppPrinter::adjustRasterSettings(QString documentFormat, QJsonObject& jobAt
|
|||
}
|
||||
}
|
||||
|
||||
HwResX = HwResY = AdjustedHwRes;
|
||||
Params.hwResW = Params.hwResH = AdjustedHwRes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -379,64 +485,105 @@ void IppPrinter::adjustRasterSettings(QString documentFormat, QJsonObject& jobAt
|
|||
|
||||
if(Sides != "" && Sides != "one-sided")
|
||||
{
|
||||
if(documentFormat == Mimer::PWG)
|
||||
if(Params.format == PrintParameters::PWG)
|
||||
{
|
||||
QString DocumentSheetBack = _attrs["pwg-raster-document-sheet-back"].toObject()["value"].toString();
|
||||
if(Sides=="two-sided-long-edge")
|
||||
{
|
||||
if(DocumentSheetBack=="flipped")
|
||||
{
|
||||
BackVFlip=true;
|
||||
Params.backVFlip=true;
|
||||
}
|
||||
else if(DocumentSheetBack=="rotated")
|
||||
{
|
||||
BackHFlip=true;
|
||||
BackVFlip=true;
|
||||
Params.backHFlip=true;
|
||||
Params.backVFlip=true;
|
||||
}
|
||||
}
|
||||
else if(Sides=="two-sided-short-edge")
|
||||
{
|
||||
if(DocumentSheetBack=="flipped")
|
||||
{
|
||||
BackHFlip=true;
|
||||
Params.backHFlip=true;
|
||||
}
|
||||
else if(DocumentSheetBack=="manual-tumble")
|
||||
{
|
||||
BackHFlip=true;
|
||||
BackVFlip=true;
|
||||
Params.backHFlip=true;
|
||||
Params.backVFlip=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(documentFormat == Mimer::URF)
|
||||
else if(Params.format == PrintParameters::URF)
|
||||
{
|
||||
QJsonArray URfSupported = _attrs["urf-supported"].toObject()["value"].toArray();
|
||||
if(Sides=="two-sided-long-edge")
|
||||
{
|
||||
if(URfSupported.contains("DM2"))
|
||||
{
|
||||
BackVFlip=true;
|
||||
Params.backVFlip=true;
|
||||
}
|
||||
else if(URfSupported.contains("DM3"))
|
||||
{
|
||||
BackHFlip=true;
|
||||
BackVFlip=true;
|
||||
Params.backHFlip=true;
|
||||
Params.backVFlip=true;
|
||||
}
|
||||
}
|
||||
else if(Sides=="two-sided-short-edge")
|
||||
{
|
||||
if(URfSupported.contains("DM2"))
|
||||
{
|
||||
BackHFlip=true;
|
||||
Params.backHFlip=true;
|
||||
}
|
||||
else if(URfSupported.contains("DM4"))
|
||||
{
|
||||
BackHFlip=true;
|
||||
BackVFlip=true;
|
||||
Params.backHFlip=true;
|
||||
Params.backVFlip=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int copies_requested = getAttrOrDefault(jobAttrs, "copies").toInt(1);
|
||||
QJsonArray varying_attributes = _attrs["document-format-varying-attributes"].toObject()["value"].toArray();
|
||||
bool supports_copies = _attrs.contains("copies-supported")
|
||||
&& _attrs["copies-supported"].toObject()["value"].toObject()["high"].toInt(1) != 1
|
||||
// assume raster formats causes the variation in supported copies
|
||||
&& !(varying_attributes.contains("copies-supported") || varying_attributes.contains("copies"));
|
||||
|
||||
if(copies_requested > 1 && !supports_copies)
|
||||
{
|
||||
QString copyMode = getAttrOrDefault(jobAttrs, "multiple-document-handling").toString();
|
||||
qDebug() << "Doing local copies" << copyMode << copies_requested;
|
||||
if(copyMode == "separate-documents-uncollated-copies")
|
||||
{ // Only do silly copies if explicitly requested
|
||||
Params.pageCopies = copies_requested;
|
||||
}
|
||||
else
|
||||
{
|
||||
Params.documentCopies = copies_requested;
|
||||
}
|
||||
jobAttrs.remove("copies");
|
||||
|
||||
|
||||
if(Sides != "one-sided")
|
||||
{
|
||||
bool singlePageRange = false;
|
||||
if(Params.pageRangeList.size() == 1)
|
||||
{
|
||||
size_t fromPage = Params.pageRangeList.begin()->first;
|
||||
size_t toPage = Params.pageRangeList.begin()->second;
|
||||
singlePageRange = fromPage != 0 && fromPage == toPage;
|
||||
}
|
||||
bool singlePageDocument = (Mimer::instance()->isImage(mimeType) ||
|
||||
(mimeType == Mimer::PDF && ConvertChecker::instance()->pdfPages(filename) == 1));
|
||||
|
||||
if(singlePageDocument || singlePageRange)
|
||||
{
|
||||
jobAttrs.insert("sides", QJsonObject {{"tag", IppMsg::Keyword}, {"value", "one-sided"}});
|
||||
qDebug() << "Optimizing one-page document to one-sided";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IppPrinter::print(QJsonObject jobAttrs, QString filename)
|
||||
|
@ -446,6 +593,7 @@ void IppPrinter::print(QJsonObject jobAttrs, QString filename)
|
|||
_progress = "";
|
||||
emit progressChanged();
|
||||
|
||||
PrintParameters Params;
|
||||
QFileInfo fileinfo(filename);
|
||||
|
||||
if(!fileinfo.exists())
|
||||
|
@ -466,22 +614,48 @@ void IppPrinter::print(QJsonObject jobAttrs, QString filename)
|
|||
|
||||
qDebug() << supportedMimeTypes << supportedMimeTypes.contains(mimeType);
|
||||
|
||||
QJsonObject o = opAttrs();
|
||||
o.insert("job-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", fileinfo.fileName()}});
|
||||
|
||||
QString PaperSize = getAttrOrDefault(jobAttrs, "media").toString();
|
||||
|
||||
if(jobAttrs.contains("media-col") && jobAttrs.contains("media"))
|
||||
if(!Params.setPaperSize(getAttrOrDefault(jobAttrs, "media").toString(Params.paperSizeName.c_str()).toStdString()))
|
||||
{
|
||||
qDebug() << "moving media to media-col" << PaperSize;
|
||||
if(!PaperSizes.contains(PaperSize))
|
||||
{
|
||||
emit convertFailed(tr("Unknown document format dimensions"));
|
||||
emit convertFailed(tr("Unsupported paper size"));
|
||||
return;
|
||||
}
|
||||
|
||||
int x = PaperSizes[PaperSize].width()*100;
|
||||
int y = PaperSizes[PaperSize].height()*100;
|
||||
QString targetFormat = getAttrOrDefault(jobAttrs, "document-format").toString();
|
||||
qDebug() << "target format:" << targetFormat;
|
||||
|
||||
QMargins margins(getAttrOrDefault(jobAttrs, "media-left-margin", "media-col").toInt(),
|
||||
getAttrOrDefault(jobAttrs, "media-top-margin", "media-col").toInt(),
|
||||
getAttrOrDefault(jobAttrs, "media-right-margin", "media-col").toInt(),
|
||||
getAttrOrDefault(jobAttrs, "media-bottom-margin", "media-col").toInt());
|
||||
|
||||
// Only keep margin setting for JPEG - but only attemt to remove it if media-col exists
|
||||
if(!mimer->isImage(targetFormat) && jobAttrs.contains("media-col"))
|
||||
{
|
||||
QJsonObject MediaCol = jobAttrs["media-col"].toObject();
|
||||
QJsonObject MediaColValue = MediaCol["value"].toObject();
|
||||
|
||||
MediaColValue.remove("media-left-margin");
|
||||
MediaColValue.remove("media-top-margin");
|
||||
MediaColValue.remove("media-right-margin");
|
||||
MediaColValue.remove("media-bottom-margin");
|
||||
|
||||
if(!MediaColValue.empty())
|
||||
{
|
||||
MediaCol["value"] = MediaColValue;
|
||||
jobAttrs["media-col"] = MediaCol;
|
||||
}
|
||||
else
|
||||
{
|
||||
jobAttrs.remove("media-col");
|
||||
}
|
||||
}
|
||||
|
||||
if(jobAttrs.contains("media-col") && jobAttrs.contains("media"))
|
||||
{
|
||||
qDebug() << "moving media to media-col" << Params.paperSizeName.c_str();
|
||||
|
||||
int x = Params.getPaperSizeWInMillimeters()*100;
|
||||
int y = Params.getPaperSizeHInMillimeters()*100;
|
||||
|
||||
QJsonObject Dimensions =
|
||||
{{"tag", IppMsg::BeginCollection},
|
||||
|
@ -500,125 +674,114 @@ void IppPrinter::print(QJsonObject jobAttrs, QString filename)
|
|||
jobAttrs.remove("media");
|
||||
}
|
||||
|
||||
QString documentFormat = getAttrOrDefault(jobAttrs, "document-format").toString();
|
||||
qDebug() << "target format:" << documentFormat;
|
||||
|
||||
QJsonObject jobOpAttrs = opAttrs();
|
||||
// document-format goes in the op-attrs and not the job-attrs
|
||||
o.insert("document-format", QJsonObject {{"tag", IppMsg::MimeMediaType}, {"value", documentFormat}});
|
||||
jobOpAttrs.insert("document-format", QJsonObject {{"tag", IppMsg::MimeMediaType}, {"value", targetFormat}});
|
||||
jobAttrs.remove("document-format");
|
||||
|
||||
documentFormat = targetFormatIfAuto(documentFormat, mimeType, supportedMimeTypes);
|
||||
qDebug() << "adjusted target format:" << documentFormat;
|
||||
targetFormat = targetFormatIfAuto(targetFormat, mimeType, supportedMimeTypes);
|
||||
qDebug() << "adjusted target format:" << targetFormat;
|
||||
|
||||
if(documentFormat == "" || documentFormat == Mimer::OctetStream)
|
||||
if(targetFormat == "" || targetFormat == Mimer::OctetStream)
|
||||
{
|
||||
emit convertFailed(tr("Unknown document format"));
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Printing job" << o << jobAttrs;
|
||||
|
||||
QJsonValue PrinterResolutionRef = getAttrOrDefault(jobAttrs, "printer-resolution");
|
||||
quint32 HwResX = PrinterResolutionRef.toObject()["x"].toInt(300);
|
||||
quint32 HwResY = PrinterResolutionRef.toObject()["y"].toInt(300);
|
||||
bool BackHFlip = false;
|
||||
bool BackVFlip = false;
|
||||
|
||||
adjustRasterSettings(documentFormat, jobAttrs, HwResX, HwResY, BackHFlip, BackVFlip);
|
||||
|
||||
quint32 Quality = getAttrOrDefault(jobAttrs, "print-quality").toInt();
|
||||
|
||||
QString PrintColorMode = getAttrOrDefault(jobAttrs, "print-color-mode").toString();
|
||||
quint32 Colors = PrintColorMode.contains("color") ? 3 : PrintColorMode.contains("monochrome") ? 1 : 0;
|
||||
|
||||
quint32 PageRangeLow = 0;
|
||||
quint32 PageRangeHigh = 0;
|
||||
if(jobAttrs.contains("page-ranges"))
|
||||
if(targetFormat == Mimer::PDF)
|
||||
{
|
||||
QJsonObject PageRanges = getAttrOrDefault(jobAttrs, "page-ranges").toObject();
|
||||
PageRangeLow = PageRanges["low"].toInt();
|
||||
PageRangeHigh = PageRanges["high"].toInt();
|
||||
// Effected locally, unless it is Postscript which we cant't render
|
||||
if(documentFormat != Mimer::Postscript)
|
||||
{
|
||||
jobAttrs.remove("page-ranges");
|
||||
Params.format = PrintParameters::PDF;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Final op attributes:" << o;
|
||||
qDebug() << "Final job attributes:" << jobAttrs;
|
||||
|
||||
IppMsg job = mk_msg(o, jobAttrs);
|
||||
QByteArray contents = job.encode(IppMsg::PrintJob);
|
||||
|
||||
// Shouldn't and can't process these formats respectively
|
||||
if((mimeType == documentFormat) && (documentFormat == Mimer::JPEG || documentFormat == Mimer::Postscript))
|
||||
else if(targetFormat == Mimer::Postscript)
|
||||
{
|
||||
emit doJustUpload(filename, contents);
|
||||
Params.format = PrintParameters::Postscript;
|
||||
}
|
||||
else if(targetFormat == Mimer::PWG)
|
||||
{
|
||||
Params.format = PrintParameters::PWG;
|
||||
}
|
||||
else if(targetFormat == Mimer::URF)
|
||||
{
|
||||
Params.format = PrintParameters::URF;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(PaperSize == "")
|
||||
{
|
||||
PaperSize = "iso_a4_210x297mm";
|
||||
}
|
||||
else if(!PaperSizes.contains(PaperSize))
|
||||
{
|
||||
emit convertFailed(tr("Unsupported print media"));
|
||||
return;
|
||||
Params.format = PrintParameters::Invalid;
|
||||
}
|
||||
|
||||
bool TwoSided = false;
|
||||
bool Tumble = false;
|
||||
qDebug() << "Printing job" << jobOpAttrs << jobAttrs;
|
||||
|
||||
QJsonValue PrinterResolutionRef = getAttrOrDefault(jobAttrs, "printer-resolution");
|
||||
Params.hwResW = PrinterResolutionRef.toObject()["x"].toInt(Params.hwResW);
|
||||
Params.hwResH = PrinterResolutionRef.toObject()["y"].toInt(Params.hwResH);
|
||||
|
||||
// Effected locally, unless it is Postscript which we cant't render
|
||||
if(jobAttrs.contains("page-ranges") && mimeType != Mimer::Postscript)
|
||||
{
|
||||
QJsonArray tmp;
|
||||
QJsonValue pageRanges = getAttrOrDefault(jobAttrs, "page-ranges");
|
||||
if(pageRanges.isArray())
|
||||
{
|
||||
tmp = pageRanges.toArray();
|
||||
}
|
||||
else if(pageRanges.isObject())
|
||||
{
|
||||
tmp = {pageRanges.toObject()};
|
||||
}
|
||||
for(QJsonValueRef ref : tmp)
|
||||
{
|
||||
Params.pageRangeList.push_back({ref.toObject()["low"].toInt(), ref.toObject()["high"].toInt()});
|
||||
}
|
||||
jobAttrs.remove("page-ranges");
|
||||
}
|
||||
|
||||
adjustRasterSettings(filename, mimeType, jobAttrs, Params);
|
||||
|
||||
QString Sides = getAttrOrDefault(jobAttrs, "sides").toString();
|
||||
|
||||
if(Sides=="two-sided-long-edge")
|
||||
{
|
||||
TwoSided = true;
|
||||
Params.duplex = true;
|
||||
}
|
||||
else if(Sides=="two-sided-short-edge")
|
||||
{
|
||||
TwoSided = true;
|
||||
Tumble = true;
|
||||
Params.duplex = true;
|
||||
Params.tumble = true;
|
||||
}
|
||||
|
||||
setBusyMessage(tr("Preparing"));
|
||||
Params.quality = getAttrOrDefault(jobAttrs, "print-quality").toInt();
|
||||
|
||||
if(mimeType == Mimer::PDF)
|
||||
{
|
||||
emit doConvertPdf(filename, contents, documentFormat, Colors, Quality,
|
||||
PaperSize, HwResX, HwResY, TwoSided, Tumble, PageRangeLow, PageRangeHigh,
|
||||
BackHFlip, BackVFlip);
|
||||
}
|
||||
else if(mimeType == Mimer::Plaintext)
|
||||
{
|
||||
emit doConvertPlaintext(filename, contents, documentFormat, Colors, Quality,
|
||||
PaperSize, HwResX, HwResY, TwoSided, Tumble, BackHFlip, BackVFlip);
|
||||
}
|
||||
else if (Mimer::isImage(mimeType))
|
||||
{
|
||||
QMargins margins(getAttrOrDefault(jobAttrs, "media-left-margin", "media-col").toInt(),
|
||||
getAttrOrDefault(jobAttrs, "media-top-margin", "media-col").toInt(),
|
||||
getAttrOrDefault(jobAttrs, "media-right-margin", "media-col").toInt(),
|
||||
getAttrOrDefault(jobAttrs, "media-bottom-margin", "media-col").toInt());
|
||||
QString PrintColorMode = getAttrOrDefault(jobAttrs, "print-color-mode").toString();
|
||||
Params.colors = PrintColorMode.contains("color") ? 3 : PrintColorMode.contains("monochrome") ? 1 : Params.colors;
|
||||
|
||||
emit doConvertImage(filename, contents, documentFormat, Colors, Quality,
|
||||
PaperSize, HwResX, HwResY, margins);
|
||||
}
|
||||
else if(Mimer::isOffice(mimeType))
|
||||
QJsonArray supportedOperations = _attrs["operations-supported"].toObject()["value"].toArray();
|
||||
|
||||
if(supportedOperations.contains(IppMsg::CreateJob) && supportedOperations.contains(IppMsg::SendDocument))
|
||||
{
|
||||
emit doConvertOfficeDocument(filename, contents, documentFormat, Colors, Quality,
|
||||
PaperSize, HwResX, HwResY, TwoSided, Tumble, PageRangeLow, PageRangeHigh,
|
||||
BackHFlip, BackVFlip);
|
||||
QJsonObject createJobOpAttrs = opAttrs();
|
||||
createJobOpAttrs.insert("job-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", fileinfo.fileName()}});
|
||||
|
||||
qDebug() << "Final create op attributes:" << createJobOpAttrs;
|
||||
qDebug() << "Final job attributes:" << jobAttrs;
|
||||
|
||||
IppMsg createJob = mk_msg(IppMsg::CreateJob, createJobOpAttrs, jobAttrs);
|
||||
qDebug() << "Final job op attributes:" << jobOpAttrs;
|
||||
|
||||
IppMsg sendDoc = mk_msg(IppMsg::SendDocument, jobOpAttrs);
|
||||
|
||||
emit doPrint2(filename, mimeType, targetFormat, createJob, sendDoc, Params, margins);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit convertFailed(tr("Cannot convert this file format"));
|
||||
return;
|
||||
}
|
||||
jobOpAttrs.insert("job-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", fileinfo.fileName()}});
|
||||
|
||||
qDebug() << "Final op attributes:" << jobOpAttrs;
|
||||
qDebug() << "Final job attributes:" << jobAttrs;
|
||||
|
||||
IppMsg job = mk_msg(IppMsg::PrintJob, jobOpAttrs, jobAttrs);
|
||||
emit doPrint(filename, mimeType, targetFormat, job, Params, margins);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool IppPrinter::getJobs() {
|
||||
|
@ -627,9 +790,9 @@ bool IppPrinter::getJobs() {
|
|||
QJsonObject o = opAttrs();
|
||||
o.insert("requested-attributes", QJsonObject {{"tag", IppMsg::Keyword}, {"value", "all"}});
|
||||
|
||||
IppMsg job = IppMsg(o, QJsonObject());
|
||||
IppMsg job = IppMsg(IppMsg::GetJobs, o, QJsonObject());
|
||||
|
||||
emit doGetJobs(job.encode(IppMsg::GetJobs));
|
||||
emit doGetJobs(job.encode());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -641,16 +804,29 @@ bool IppPrinter::cancelJob(qint32 jobId) {
|
|||
QJsonObject o = opAttrs();
|
||||
o.insert("job-id", QJsonObject {{"tag", IppMsg::Integer}, {"value", jobId}});
|
||||
|
||||
IppMsg job = IppMsg(o, QJsonObject());
|
||||
IppMsg job = IppMsg(IppMsg::CancelJob, o, QJsonObject());
|
||||
|
||||
emit doCancelJob(job.encode(IppMsg::CancelJob));
|
||||
emit doCancelJob(job.encode());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IppPrinter::identify() {
|
||||
|
||||
qDebug() << "identifying";
|
||||
|
||||
QJsonObject o = opAttrs();
|
||||
|
||||
IppMsg job = IppMsg(IppMsg::IdentifyPrinter, o, QJsonObject());
|
||||
|
||||
emit doCancelJob(job.encode());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IppPrinter::correctSuffix()
|
||||
{
|
||||
foreach(QJsonValue u, _attrs["printer-uri-supported"].toObject()["value"].toArray())
|
||||
for(QJsonValue u : _attrs["printer-uri-supported"].toObject()["value"].toArray())
|
||||
{
|
||||
QUrl url(u.toString());
|
||||
if(url.path() == _url.path())
|
||||
|
@ -664,7 +840,7 @@ bool IppPrinter::correctSuffix()
|
|||
QStringList IppPrinter::suffixes()
|
||||
{
|
||||
QStringList res;
|
||||
foreach(QJsonValue u, _attrs["printer-uri-supported"].toObject()["value"].toArray())
|
||||
for(QJsonValue u : _attrs["printer-uri-supported"].toObject()["value"].toArray())
|
||||
{
|
||||
QUrl url(u.toString());
|
||||
if(!res.contains(url.path()))
|
||||
|
@ -712,6 +888,13 @@ void IppPrinter::setProgress(qint64 sent, qint64 total)
|
|||
emit progressChanged();
|
||||
}
|
||||
|
||||
bool IppPrinter::isAllowedAddress(QUrl url)
|
||||
{
|
||||
bool allowed = ((url.host() == _url.host()) || Settings::instance()->allowExternalConnections());
|
||||
qDebug() << url << " is allowed: " << allowed;
|
||||
return allowed;
|
||||
}
|
||||
|
||||
QJsonValue IppPrinter::getAttrOrDefault(QJsonObject jobAttrs, QString name, QString subkey)
|
||||
{
|
||||
if(subkey == "")
|
||||
|
@ -739,13 +922,34 @@ QJsonValue IppPrinter::getAttrOrDefault(QJsonObject jobAttrs, QString name, QStr
|
|||
}
|
||||
}
|
||||
|
||||
IppMsg IppPrinter::mk_msg(QJsonObject opAttrs, QJsonObject jobAttrs)
|
||||
IppMsg IppPrinter::mk_msg(IppMsg::Operation operation, QJsonObject opAttrs, QJsonObject jobAttrs)
|
||||
{
|
||||
if(_attrs.contains("ipp-versions-supported") &&
|
||||
_attrs["ipp-versions-supported"].toObject()["value"].toArray().contains("2.0"))
|
||||
{
|
||||
qDebug() << "TWO-POINT-ZERO";
|
||||
return IppMsg(opAttrs, jobAttrs, 2, 0);
|
||||
return IppMsg(operation, opAttrs, jobAttrs, 2, 0);
|
||||
}
|
||||
return IppMsg(operation, opAttrs, jobAttrs);
|
||||
}
|
||||
|
||||
void IppPrinter::resolveUrl(QUrl& url)
|
||||
{
|
||||
if(!IppDiscovery::instance()->resolve(url))
|
||||
{ // If "proper" resolution fails, cheat...
|
||||
QString host = url.host();
|
||||
|
||||
if(host.endsWith("."))
|
||||
{
|
||||
host.chop(1);
|
||||
}
|
||||
|
||||
QString dnsSdName = _attrs["printer-dns-sd-name"].toObject()["value"].toString();
|
||||
dnsSdName = dnsSdName.append(".local");
|
||||
|
||||
if(host.compare(dnsSdName, Qt::CaseInsensitive) == 0)
|
||||
{ // This could be done unconditionally, but some might want their externally hosted stuff to work
|
||||
url.setHost(_url.host());
|
||||
}
|
||||
}
|
||||
return IppMsg(opAttrs, jobAttrs);
|
||||
}
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
#ifndef IPPPRINTER_H
|
||||
#define IPPPRINTER_H
|
||||
|
||||
#include <QtNetwork>
|
||||
#include <QNetworkAccessManager>
|
||||
#include "ippmsg.h"
|
||||
#include "convertworker.h"
|
||||
#include "printerworker.h"
|
||||
#include "curlrequester.h"
|
||||
#include <mlite5/MGConfItem>
|
||||
#include <QImage>
|
||||
|
||||
|
||||
class IppPrinter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class ConvertWorker;
|
||||
friend class PrinterWorker;
|
||||
|
||||
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)
|
||||
Q_PROPERTY(QJsonArray jobs MEMBER _jobs NOTIFY jobsChanged)
|
||||
Q_PROPERTY(QJsonObject strings MEMBER _strings NOTIFY stringsChanged)
|
||||
Q_PROPERTY(QImage icon MEMBER _icon NOTIFY iconChanged)
|
||||
Q_PROPERTY(QStringList additionalDocumentFormats MEMBER _additionalDocumentFormats NOTIFY additionalDocumentFormatsChanged)
|
||||
Q_PROPERTY(QString busyMessage MEMBER _busyMessage NOTIFY busyMessageChanged)
|
||||
Q_PROPERTY(QString progress MEMBER _progress NOTIFY progressChanged)
|
||||
|
@ -37,6 +38,7 @@ public:
|
|||
|
||||
Q_INVOKABLE bool getJobs();
|
||||
Q_INVOKABLE bool cancelJob(qint32 jobId);
|
||||
Q_INVOKABLE bool identify();
|
||||
|
||||
bool correctSuffix();
|
||||
QStringList suffixes();
|
||||
|
@ -47,32 +49,34 @@ signals:
|
|||
void jobAttrsChanged();
|
||||
void jobsChanged();
|
||||
|
||||
void stringsChanged();
|
||||
void iconChanged();
|
||||
|
||||
void jobFinished(bool status);
|
||||
void cancelStatus(bool status);
|
||||
|
||||
void doCommand(QByteArray msg);
|
||||
void doGetJobs(QByteArray msg);
|
||||
void doCancelJob(QByteArray msg);
|
||||
void doDoGetPrinterAttributes(Bytestream msg);
|
||||
void doGetJobs(Bytestream msg);
|
||||
void doCancelJob(Bytestream msg);
|
||||
void doIdentify(Bytestream msg);
|
||||
|
||||
void doJustUpload(QString filename, QByteArray header);
|
||||
void doJustUpload(QString filename, Bytestream header);
|
||||
void doFixupPlaintext(QString filename, Bytestream header);
|
||||
void doPrintImageAsImage(QString filename, Bytestream header, QString targetFormat);
|
||||
|
||||
void doConvertPdf(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
|
||||
quint32 PageRangeLow, quint32 PageRangeHigh, bool BackHFlip, bool BackVFlip);
|
||||
void doConvertPdf(QString filename, Bytestream header, PrintParameters Params);
|
||||
|
||||
void doConvertImage(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, QMargins margins);
|
||||
void doConvertImage(QString filename, Bytestream header, PrintParameters Params, QMargins margins);
|
||||
|
||||
void doConvertOfficeDocument(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
|
||||
quint32 PageRangeLow, quint32 PageRangeHigh, bool BackHFlip, bool BackVFlip);
|
||||
void doConvertOfficeDocument(QString filename, Bytestream header, PrintParameters Params);
|
||||
|
||||
void doConvertPlaintext(QString filename, QByteArray header,
|
||||
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
|
||||
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble, bool BackHFlip, bool BackVFlip);
|
||||
void doConvertPlaintext(QString filename, Bytestream header, PrintParameters Params);
|
||||
|
||||
void doPrint(QString filename, QString mimeType, QString targetFormat, IppMsg job, PrintParameters Params, QMargins margins);
|
||||
void doPrint2(QString filename, QString mimeType, QString targetFormat, IppMsg createJob, IppMsg sendDocument, PrintParameters Params, QMargins margins);
|
||||
|
||||
void doGetStrings(QUrl url);
|
||||
void doGetImage(QUrl url);
|
||||
|
||||
void additionalDocumentFormatsChanged();
|
||||
void busyMessageChanged();
|
||||
|
@ -81,46 +85,54 @@ signals:
|
|||
public slots:
|
||||
void print(QJsonObject attrs, QString file);
|
||||
|
||||
|
||||
void onUrlChanged();
|
||||
void MaybeGetStrings();
|
||||
void MaybeGetIcon(bool retry=false);
|
||||
void UpdateAdditionalDocumentFormats();
|
||||
void getPrinterAttributesFinished(CURLcode res, QByteArray data);
|
||||
void printRequestFinished(CURLcode res, QByteArray data);
|
||||
void getJobsRequestFinished(CURLcode res, QByteArray data);
|
||||
void cancelJobFinished(CURLcode res, QByteArray data);
|
||||
void getPrinterAttributesFinished(CURLcode res, Bytestream data);
|
||||
void printRequestFinished(CURLcode res, Bytestream data);
|
||||
void getJobsRequestFinished(CURLcode res, Bytestream data);
|
||||
void cancelJobFinished(CURLcode res, Bytestream data);
|
||||
void identifyFinished(CURLcode res, Bytestream data);
|
||||
|
||||
static void ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
||||
void getStringsFinished(CURLcode res, Bytestream data);
|
||||
void getImageFinished(CURLcode res, Bytestream data);
|
||||
|
||||
void convertFailed(QString message);
|
||||
|
||||
private:
|
||||
QUrl _url;
|
||||
QUrl httpUrl();
|
||||
void resolveUrl(QUrl& url);
|
||||
|
||||
QJsonObject opAttrs();
|
||||
|
||||
void adjustRasterSettings(QString documentFormat, QJsonObject& jobAttrs, quint32& HwResX, quint32& HwResY,
|
||||
bool& BackHFlip, bool& BackVFlip);
|
||||
void adjustRasterSettings(QString filename, QString mimeType, QJsonObject& jobAttrs, PrintParameters& Params);
|
||||
|
||||
void setBusyMessage(QString msg);
|
||||
void setProgress(qint64 sent, qint64 total);
|
||||
|
||||
bool isAllowedAddress(QUrl addr);
|
||||
|
||||
QJsonValue getAttrOrDefault(QJsonObject jobAttrs, QString name, QString subkey = "");
|
||||
|
||||
IppMsg mk_msg(QJsonObject opAttrs, QJsonObject jobAttrs=QJsonObject());
|
||||
IppMsg mk_msg(IppMsg::Operation operation, QJsonObject opAttrs, QJsonObject jobAttrs=QJsonObject());
|
||||
|
||||
QJsonObject _attrs;
|
||||
QJsonObject _jobAttrs;
|
||||
QJsonArray _jobs;
|
||||
|
||||
QJsonObject _strings;
|
||||
QImage _icon;
|
||||
|
||||
QStringList _additionalDocumentFormats;
|
||||
|
||||
QString _busyMessage;
|
||||
QString _progress;
|
||||
|
||||
QThread _workerThread;
|
||||
ConvertWorker* _worker;
|
||||
PrinterWorker* _worker;
|
||||
|
||||
bool _iconRetried = false;
|
||||
};
|
||||
|
||||
#endif // IPPPRINTER_H
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "mimer.h"
|
||||
#include <QDebug>
|
||||
|
||||
const QString Mimer::OctetStream = "application/octet-stream";
|
||||
|
||||
|
@ -11,16 +12,22 @@ const QString Mimer::PNG = "image/png";
|
|||
const QString Mimer::GIF = "image/gif";
|
||||
const QString Mimer::JPEG = "image/jpeg";
|
||||
const QString Mimer::TIFF = "image/tiff";
|
||||
const QString Mimer::SVG = "image/svg+xml";
|
||||
const QString Mimer::RBMP = "image/reverse-encoding-bmp";
|
||||
|
||||
const QString Mimer::DOC = "application/msword";
|
||||
const QString Mimer::DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
const QString Mimer::RTF = "text/rtf";
|
||||
const QString Mimer::RTF_APP = "application/rtf";
|
||||
const QString Mimer::ODT = "application/vnd.oasis.opendocument.text";
|
||||
const QString Mimer::PPT = "application/vnd.ms-powerpoint";
|
||||
const QString Mimer::PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
||||
const QString Mimer::ODP = "application/vnd.oasis.opendocument.presentation";
|
||||
|
||||
const QString Mimer::Plaintext = "text/plain";
|
||||
|
||||
const QStringList Mimer::OfficeFormats = {DOC, DOCX, RTF, RTF_APP, ODT};
|
||||
const QStringList Mimer::RasterFormats = {PWG, URF};
|
||||
const QStringList Mimer::OfficeFormats = {DOC, DOCX, RTF, RTF_APP, ODT, PPT, PPTX, ODP};
|
||||
|
||||
Mimer::Mimer()
|
||||
{
|
||||
|
@ -46,5 +53,14 @@ Mimer* Mimer::instance()
|
|||
}
|
||||
|
||||
QString Mimer::get_type(QString filename) {
|
||||
return _db.mimeTypeForFile(filename).name();
|
||||
QString type = _db.mimeTypeForFile(filename).name();
|
||||
qDebug() << "MimeType:" << type;
|
||||
if(type == PDF || type == Postscript || type == Plaintext || isImage(type) || isOffice(type))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
18
src/mimer.h
18
src/mimer.h
|
@ -24,11 +24,17 @@ public:
|
|||
Q_PROPERTY(const QString GIF MEMBER GIF CONSTANT);
|
||||
Q_PROPERTY(const QString JPEG MEMBER JPEG CONSTANT);
|
||||
Q_PROPERTY(const QString TIFF MEMBER TIFF CONSTANT);
|
||||
Q_PROPERTY(const QString SVG MEMBER SVG CONSTANT);
|
||||
Q_PROPERTY(const QString RBMP MEMBER RBMP CONSTANT);
|
||||
|
||||
Q_PROPERTY(const QString DOC MEMBER DOC CONSTANT);
|
||||
Q_PROPERTY(const QString DOCX MEMBER DOCX CONSTANT);
|
||||
Q_PROPERTY(const QString RTF MEMBER RTF CONSTANT);
|
||||
Q_PROPERTY(const QString ODT MEMBER ODT CONSTANT);
|
||||
Q_PROPERTY(const QString PPT MEMBER PPT CONSTANT);
|
||||
Q_PROPERTY(const QString PPTX MEMBER PPTX CONSTANT);
|
||||
Q_PROPERTY(const QString ODP MEMBER ODP CONSTANT);
|
||||
|
||||
|
||||
Q_PROPERTY(const QString Plaintext MEMBER Plaintext CONSTANT);
|
||||
|
||||
|
@ -45,20 +51,30 @@ public:
|
|||
static const QString GIF;
|
||||
static const QString JPEG;
|
||||
static const QString TIFF;
|
||||
static const QString SVG;
|
||||
static const QString RBMP;
|
||||
|
||||
static const QString DOC;
|
||||
static const QString DOCX;
|
||||
static const QString RTF;
|
||||
static const QString RTF_APP;
|
||||
static const QString ODT;
|
||||
static const QString PPT;
|
||||
static const QString PPTX;
|
||||
static const QString ODP;
|
||||
|
||||
static const QString Plaintext;
|
||||
|
||||
static const QStringList RasterFormats;
|
||||
static const QStringList OfficeFormats;
|
||||
|
||||
Q_INVOKABLE static bool isImage(QString mimeType)
|
||||
{
|
||||
return mimeType.startsWith("image/");
|
||||
return mimeType.startsWith("image/") && ! isRaster(mimeType);
|
||||
}
|
||||
Q_INVOKABLE static bool isRaster(QString mimeType)
|
||||
{
|
||||
return RasterFormats.contains(mimeType);
|
||||
}
|
||||
Q_INVOKABLE static bool isOffice(QString mimeType)
|
||||
{
|
||||
|
|
|
@ -40,7 +40,7 @@ Overrider* Overrider::instance()
|
|||
bool Overrider::apply(QJsonObject& attrs)
|
||||
{
|
||||
bool applied = false;
|
||||
foreach(const QString& key, _overrides.keys())
|
||||
for(const QString& key : _overrides.keys())
|
||||
{
|
||||
if(attrs.contains(key))
|
||||
{
|
||||
|
@ -48,7 +48,7 @@ bool Overrider::apply(QJsonObject& attrs)
|
|||
if(match != "" && _overrides[key].toObject().contains(match))
|
||||
{
|
||||
QJsonObject overrideden_data = _overrides[key].toObject()[match].toObject();
|
||||
foreach(const QString& o_key, overrideden_data.keys())
|
||||
for(const QString& o_key : overrideden_data.keys())
|
||||
{
|
||||
attrs.insert(o_key, overrideden_data[o_key]);
|
||||
}
|
||||
|
|
202
src/papersizes.h
202
src/papersizes.h
|
@ -3,208 +3,6 @@
|
|||
#include <QMap>
|
||||
#include <QSizeF>
|
||||
|
||||
static QMap<QString, QSizeF> PaperSizes =
|
||||
{{"asme_f_28x40in", {711.20, 1016.00}},
|
||||
{"iso_2a0_1189x1682mm", {1189.00, 1682.00}},
|
||||
{"iso_a0_841x1189mm", {841.00, 1189.00}},
|
||||
{"iso_a0x3_1189x2523mm", {1189.00, 2523.00}},
|
||||
{"iso_a10_26x37mm", {26.00, 37.00}},
|
||||
{"iso_a1_594x841mm", {594.00, 841.00}},
|
||||
{"iso_a1x3_841x1783mm", {841.00, 1783.00}},
|
||||
{"iso_a1x4_841x2378mm", {841.00, 2378.00}},
|
||||
{"iso_a2_420x594mm", {420.00, 594.00}},
|
||||
{"iso_a2x3_594x1261mm", {594.00, 1261.00}},
|
||||
{"iso_a2x4_594x1682mm", {594.00, 1682.00}},
|
||||
{"iso_a2x5_594x2102mm", {594.00, 2102.00}},
|
||||
{"iso_a3-extra_322x445mm", {322.00, 445.00}},
|
||||
{"iso_a3_297x420mm", {297.00, 420.00}},
|
||||
{"iso_a3x3_420x891mm", {420.00, 891.00}},
|
||||
{"iso_a3x4_420x1189mm", {420.00, 1189.00}},
|
||||
{"iso_a3x5_420x1486mm", {420.00, 1486.00}},
|
||||
{"iso_a3x6_420x1783mm", {420.00, 1783.00}},
|
||||
{"iso_a3x7_420x2080mm", {420.00, 2080.00}},
|
||||
{"iso_a4-extra_235.5x322.3m", {235.50, 322.30}},
|
||||
{"iso_a4-tab_225x297mm", {225.00, 297.00}},
|
||||
{"iso_a4_210x297mm", {210.00, 297.00}},
|
||||
{"iso_a4x3_297x630mm", {297.00, 630.00}},
|
||||
{"iso_a4x4_297x841mm", {297.00, 841.00}},
|
||||
{"iso_a4x5_297x1051mm", {297.00, 1051.00}},
|
||||
{"iso_a4x6_297x1261mm", {297.00, 1261.00}},
|
||||
{"iso_a4x7_297x1471mm", {297.00, 1471.00}},
|
||||
{"iso_a4x8_297x1682mm", {297.00, 1682.00}},
|
||||
{"iso_a4x9_297x1892mm", {297.00, 1892.00}},
|
||||
{"iso_a5-extra_174x235mm", {174.00, 235.00}},
|
||||
{"iso_a5_148x210mm", {148.00, 210.00}},
|
||||
{"iso_a6_105x148mm", {105.00, 148.00}},
|
||||
{"iso_a7_74x105mm", {74.00, 105.00}},
|
||||
{"iso_a8_52x74mm", {52.00, 74.00}},
|
||||
{"iso_a9_37x52mm", {37.00, 52.00}},
|
||||
{"iso_b0_1000x1414mm", {1000.00, 1414.00}},
|
||||
{"iso_b10_31x44mm", {31.00, 44.00}},
|
||||
{"iso_b1_707x1000mm", {707.00, 1000.00}},
|
||||
{"iso_b2_500x707mm", {500.00, 707.00}},
|
||||
{"iso_b3_353x500mm", {353.00, 500.00}},
|
||||
{"iso_b4_250x353mm", {250.00, 353.00}},
|
||||
{"iso_b5-extra_201x276mm", {201.00, 276.00}},
|
||||
{"iso_b5_176x250mm", {176.00, 250.00}},
|
||||
{"iso_b6_125x176mm", {125.00, 176.00}},
|
||||
{"iso_b6c4_125x324mm", {125.00, 324.00}},
|
||||
{"iso_b7_88x125mm", {88.00, 125.00}},
|
||||
{"iso_b8_62x88mm", {62.00, 88.00}},
|
||||
{"iso_b9_44x62mm", {44.00, 62.00}},
|
||||
{"iso_c0_917x1297mm", {917.00, 1297.00}},
|
||||
{"iso_c10_28x40mm", {28.00, 40.00}},
|
||||
{"iso_c1_648x917mm", {648.00, 917.00}},
|
||||
{"iso_c2_458x648mm", {458.00, 648.00}},
|
||||
{"iso_c3_324x458mm", {324.00, 458.00}},
|
||||
{"iso_c4_229x324mm", {229.00, 324.00}},
|
||||
{"iso_c5_162x229mm", {162.00, 229.00}},
|
||||
{"iso_c6_114x162mm", {114.00, 162.00}},
|
||||
{"iso_c6c5_114x229mm", {114.00, 229.00}},
|
||||
{"iso_c7_81x114mm", {81.00, 114.00}},
|
||||
{"iso_c7c6_81x162mm", {81.00, 162.00}},
|
||||
{"iso_c8_57x81mm", {57.00, 81.00}},
|
||||
{"iso_c9_40x57mm", {40.00, 57.00}},
|
||||
{"iso_dl_110x220mm", {110.00, 220.00}},
|
||||
{"iso_id-1_53.98x85.6mm", {53.98, 85.60}},
|
||||
{"iso_id-3_88x125mm", {88.00, 125.00}},
|
||||
{"iso_ra0_860x1220mm", {860.00, 1220.00}},
|
||||
{"iso_ra1_610x860mm", {610.00, 860.00}},
|
||||
{"iso_ra2_430x610mm", {430.00, 610.00}},
|
||||
{"iso_ra3_305x430mm", {305.00, 430.00}},
|
||||
{"iso_ra4_215x305mm", {215.00, 305.00}},
|
||||
{"iso_sra0_900x1280mm", {900.00, 1280.00}},
|
||||
{"iso_sra1_640x900mm", {640.00, 900.00}},
|
||||
{"iso_sra2_450x640mm", {450.00, 640.00}},
|
||||
{"iso_sra3_320x450mm", {320.00, 450.00}},
|
||||
{"iso_sra4_225x320mm", {225.00, 320.00}},
|
||||
{"jis_b0_1030x1456mm", {1030.00, 1456.00}},
|
||||
{"jis_b10_32x45mm", {32.00, 45.00}},
|
||||
{"jis_b1_728x1030mm", {728.00, 1030.00}},
|
||||
{"jis_b2_515x728mm", {515.00, 728.00}},
|
||||
{"jis_b3_364x515mm", {364.00, 515.00}},
|
||||
{"jis_b4_257x364mm", {257.00, 364.00}},
|
||||
{"jis_b5_182x257mm", {182.00, 257.00}},
|
||||
{"jis_b6_128x182mm", {128.00, 182.00}},
|
||||
{"jis_b7_91x128mm", {91.00, 128.00}},
|
||||
{"jis_b8_64x91mm", {64.00, 91.00}},
|
||||
{"jis_b9_45x64mm", {45.00, 64.00}},
|
||||
{"jis_exec_216x330mm", {216.00, 330.00}},
|
||||
{"jpn_chou2_111.1x146mm", {111.10, 146.00}},
|
||||
{"jpn_chou3_120x235mm", {120.00, 235.00}},
|
||||
{"jpn_chou40_90x225mm", {90.00, 225.00}},
|
||||
{"jpn_chou4_90x205mm", {90.00, 205.00}},
|
||||
{"jpn_hagaki_100x148mm", {100.00, 148.00}},
|
||||
{"jpn_kahu_240x322.1mm", {240.00, 322.10}},
|
||||
{"jpn_kaku1_270x382mm", {270.00, 382.00}},
|
||||
{"jpn_kaku2_240x332mm", {240.00, 332.00}},
|
||||
{"jpn_kaku3_216x277mm", {216.00, 277.00}},
|
||||
{"jpn_kaku4_197x267mm", {197.00, 267.00}},
|
||||
{"jpn_kaku5_190x240mm", {190.00, 240.00}},
|
||||
{"jpn_kaku7_142x205mm", {142.00, 205.00}},
|
||||
{"jpn_kaku8_119x197mm", {119.00, 197.00}},
|
||||
{"jpn_oufuku_148x200mm", {148.00, 200.00}},
|
||||
{"jpn_you4_105x235mm", {105.00, 235.00}},
|
||||
{"na_10x11_10x11in", {254.00, 279.40}},
|
||||
{"na_10x13_10x13in", {254.00, 330.20}},
|
||||
{"na_10x14_10x14in", {254.00, 355.60}},
|
||||
{"na_10x15_10x15in", {254.00, 381.00}},
|
||||
{"na_11x12_11x12in", {279.40, 304.80}},
|
||||
{"na_11x15_11x15in", {279.40, 381.00}},
|
||||
{"na_12x19_12x19in", {304.80, 482.60}},
|
||||
{"na_5x7_5x7in", {127.00, 177.80}},
|
||||
{"na_6x9_6x9in", {152.40, 228.60}},
|
||||
{"na_7x9_7x9in", {177.80, 228.60}},
|
||||
{"na_9x11_9x11in", {228.60, 279.40}},
|
||||
{"na_a2_4.375x5.75in", {111.12, 146.05}},
|
||||
{"na_arch-a_9x12in", {228.60, 304.80}},
|
||||
{"na_arch-b_12x18in", {304.80, 457.20}},
|
||||
{"na_arch-c_18x24in", {457.20, 609.60}},
|
||||
{"na_arch-d_24x36in", {609.60, 914.40}},
|
||||
{"na_arch-e2_26x38in", {660.40, 965.20}},
|
||||
{"na_arch-e3_27x39in", {685.80, 990.60}},
|
||||
{"na_arch-e_36x48in", {914.40, 1219.20}},
|
||||
{"na_b-plus_12x19.17in", {304.80, 486.92}},
|
||||
{"na_c5_6.5x9.5in", {165.10, 241.30}},
|
||||
{"na_c_17x22in", {431.80, 558.80}},
|
||||
{"na_d_22x34in", {558.80, 863.60}},
|
||||
{"na_e_34x44in", {863.60, 1117.60}},
|
||||
{"na_edp_11x14in", {279.40, 355.60}},
|
||||
{"na_eur-edp_12x14in", {304.80, 355.60}},
|
||||
{"na_executive_7.25x10.5in", {184.15, 266.70}},
|
||||
{"na_f_44x68in", {1117.60, 1727.20}},
|
||||
{"na_fanfold-eur_8.5x12in", {215.90, 304.80}},
|
||||
{"na_fanfold-us_11x14.875in", {279.40, 377.82}},
|
||||
{"na_foolscap_8.5x13in", {215.90, 330.20}},
|
||||
{"na_govt-legal_8x13in", {203.20, 330.20}},
|
||||
{"na_govt-letter_8x10in", {203.20, 254.00}},
|
||||
{"na_index-3x5_3x5in", {76.20, 127.00}},
|
||||
{"na_index-4x6-ext_6x8in", {152.40, 203.20}},
|
||||
{"na_index-4x6_4x6in", {101.60, 152.40}},
|
||||
{"na_index-5x8_5x8in", {127.00, 203.20}},
|
||||
{"na_invoice_5.5x8.5in", {139.70, 215.90}},
|
||||
{"na_ledger_11x17in", {279.40, 431.80}},
|
||||
{"na_legal-extra_9.5x15in", {241.30, 381.00}},
|
||||
{"na_legal_8.5x14in", {215.90, 355.60}},
|
||||
{"na_letter-extra_9.5x12in", {241.30, 304.80}},
|
||||
{"na_letter-plus_8.5x12.69in", {215.90, 322.33}},
|
||||
{"na_letter_8.5x11in", {215.90, 279.40}},
|
||||
{"na_monarch_3.875x7.5in", {98.42, 190.50}},
|
||||
{"na_number-10_4.125x9.5in", {104.77, 241.30}},
|
||||
{"na_number-11_4.5x10.375in", {114.30, 263.52}},
|
||||
{"na_number-12_4.75x11in", {120.65, 279.40}},
|
||||
{"na_number-14_5x11.5in", {127.00, 292.10}},
|
||||
{"na_number-9_3.875x8.875in", {98.42, 225.42}},
|
||||
{"na_oficio_8.5x13.4in", {215.90, 340.36}},
|
||||
{"na_personal_3.625x6.5in", {92.07, 165.10}},
|
||||
{"na_quarto_8.5x10.83in", {215.90, 275.08}},
|
||||
{"na_super-a_8.94x14in", {227.08, 355.60}},
|
||||
{"na_super-b_13x19in", {330.20, 482.60}},
|
||||
{"na_wide-format_30x42in", {762.00, 1066.80}},
|
||||
{"oe_12x16_12x16in", {304.80, 406.40}},
|
||||
{"oe_14x17_14x17in", {355.60, 431.80}},
|
||||
{"oe_18x22_18x22in", {457.20, 558.80}},
|
||||
{"oe_a2plus_17x24in", {431.80, 609.60}},
|
||||
{"oe_business-card_2x3.5in", {50.80, 88.90}},
|
||||
{"oe_photo-10r_10x12in", {254.00, 304.80}},
|
||||
{"oe_photo-20r_20x24in", {508.00, 609.60}},
|
||||
{"oe_photo-l_3.5x5in", {88.90, 127.00}},
|
||||
{"oe_photo-s10r_10x15in", {254.00, 381.00}},
|
||||
{"oe_square-photo_4x4in", {101.60, 101.60}},
|
||||
{"oe_square-photo_5x5in", {127.00, 127.00}},
|
||||
{"om_16k_184x260mm", {184.00, 260.00}},
|
||||
{"om_16k_195x270mm", {195.00, 270.00}},
|
||||
{"om_business-card_55x85mm", {55.00, 85.00}},
|
||||
{"om_business-card_55x91mm", {55.00, 91.00}},
|
||||
{"om_card_54x86mm", {54.00, 86.00}},
|
||||
{"om_dai-pa-kai_275x395mm", {275.00, 395.00}},
|
||||
{"om_dsc-photo_89x119mm", {89.00, 119.00}},
|
||||
{"om_folio-sp_215x315mm", {215.00, 315.00}},
|
||||
{"om_folio_210x330mm", {210.00, 330.00}},
|
||||
{"om_invite_220x220mm", {220.00, 220.00}},
|
||||
{"om_italian_110x230mm", {110.00, 230.00}},
|
||||
{"om_juuro-ku-kai_198x275mm", {198.00, 275.00}},
|
||||
{"om_large-photo_200x300", {200.00, 300.00}},
|
||||
{"om_medium-photo_130x180mm", {130.00, 180.00}},
|
||||
{"om_pa-kai_267x389mm", {267.00, 389.00}},
|
||||
{"om_postfix_114x229mm", {114.00, 229.00}},
|
||||
{"om_small-photo_100x150mm", {100.00, 150.00}},
|
||||
{"om_square-photo_89x89mm", {89.00, 89.00}},
|
||||
{"om_wide-photo_100x200mm", {100.00, 200.00}},
|
||||
{"prc_10_324x458mm", {324.00, 458.00}},
|
||||
{"prc_16k_146x215mm", {146.00, 215.00}},
|
||||
{"prc_1_102x165mm", {102.00, 165.00}},
|
||||
{"prc_2_102x176mm", {102.00, 176.00}},
|
||||
{"prc_32k_97x151mm", {97.00, 151.00}},
|
||||
{"prc_3_125x176mm", {125.00, 176.00}},
|
||||
{"prc_4_110x208mm", {110.00, 208.00}},
|
||||
{"prc_5_110x220mm", {110.00, 220.00}},
|
||||
{"prc_6_120x320mm", {120.00, 320.00}},
|
||||
{"prc_7_160x230mm", {160.00, 230.00}},
|
||||
{"prc_8_120x309mm", {120.00, 309.00}},
|
||||
{"roc_16k_7.75x10.75in", {196.85, 273.05}},
|
||||
{"roc_8k_10.75x15.5in", {273.05, 393.70}}};
|
||||
|
||||
static QMap<QString, QString> CalligraPaperSizes =
|
||||
{{"iso_a0_841x1189mm", "A0"},
|
||||
{"iso_a1_594x841mm", "A1"},
|
||||
|
|
812
src/printerworker.cpp
Normal file
812
src/printerworker.cpp
Normal file
|
@ -0,0 +1,812 @@
|
|||
#include "printerworker.h"
|
||||
#include "papersizes.h"
|
||||
#include "convertchecker.h"
|
||||
#include "mimer.h"
|
||||
#include <QImage>
|
||||
#include <QMatrix>
|
||||
#include <QPainter>
|
||||
#include <QTextDocument>
|
||||
#include <QPdfWriter>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QtSvg>
|
||||
#include "ippprinter.h"
|
||||
#include "pdf2printable.h"
|
||||
#include "ppm2pwg.h"
|
||||
#include "baselinify.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#define OK(call) if(!(call)) throw ConvertFailedException()
|
||||
|
||||
PrinterWorker::PrinterWorker(IppPrinter* parent)
|
||||
{
|
||||
_printer = parent;
|
||||
_url = parent->httpUrl();
|
||||
_thread.reset(new QThread);
|
||||
moveToThread(_thread.get());
|
||||
_thread->start();
|
||||
}
|
||||
|
||||
PrinterWorker::~PrinterWorker()
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "cleanup");
|
||||
_thread->wait();
|
||||
}
|
||||
|
||||
void PrinterWorker::cleanup()
|
||||
{
|
||||
_thread->quit();
|
||||
}
|
||||
|
||||
void PrinterWorker::urlChanged(QUrl url)
|
||||
{
|
||||
_url = url;
|
||||
}
|
||||
|
||||
void PrinterWorker::getStrings(QUrl url)
|
||||
{
|
||||
CurlRequester cr(url, CurlRequester::HttpGetRequest);
|
||||
awaitResult(cr, "getStringsFinished");
|
||||
}
|
||||
|
||||
void PrinterWorker::getImage(QUrl url)
|
||||
{
|
||||
CurlRequester cr(url, CurlRequester::HttpGetRequest);
|
||||
awaitResult(cr, "getImageFinished");
|
||||
}
|
||||
|
||||
|
||||
void PrinterWorker::getPrinterAttributes(Bytestream msg)
|
||||
{
|
||||
CurlRequester cr(_url, CurlRequester::IppRequest, &msg);
|
||||
awaitResult(cr, "getPrinterAttributesFinished");
|
||||
}
|
||||
|
||||
void PrinterWorker::getJobs(Bytestream msg)
|
||||
{
|
||||
CurlRequester cr(_url, CurlRequester::IppRequest, &msg);
|
||||
awaitResult(cr, "getJobsRequestFinished");
|
||||
}
|
||||
|
||||
void PrinterWorker::cancelJob(Bytestream msg)
|
||||
{
|
||||
CurlRequester cr(_url, CurlRequester::IppRequest, &msg);
|
||||
awaitResult(cr, "cancelJobFinished");
|
||||
}
|
||||
|
||||
void PrinterWorker::identify(Bytestream msg)
|
||||
{
|
||||
CurlRequester cr(_url, CurlRequester::IppRequest, &msg);
|
||||
awaitResult(cr, "identifyFinished");
|
||||
}
|
||||
|
||||
void PrinterWorker::print2(QString filename, QString mimeType, QString targetFormat, IppMsg createJob, IppMsg sendDocument, PrintParameters Params, QMargins margins)
|
||||
{
|
||||
emit busyMessage(tr("Preparing"));
|
||||
|
||||
Bytestream header = createJob.encode();
|
||||
|
||||
CurlRequester cr(_url, CurlRequester::IppRequest, &header);
|
||||
|
||||
Bytestream resData;
|
||||
CURLcode res = cr.await(&resData);
|
||||
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
IppMsg resMsg(resData);
|
||||
qDebug() << resMsg.getOpAttrs() << resMsg.getJobAttrs();
|
||||
|
||||
QJsonObject resJobAttrs = resMsg.getJobAttrs()[0].toObject();
|
||||
if(resMsg.getStatus() <= 0xff && resJobAttrs.contains("job-id"))
|
||||
{
|
||||
int jobId = resJobAttrs["job-id"].toObject()["value"].toInt();
|
||||
sendDocument.setOpAttr("job-id", IppMsg::Integer, jobId);
|
||||
sendDocument.setOpAttr("last-document", IppMsg::Boolean, true);
|
||||
print(filename, mimeType, targetFormat, sendDocument, Params, margins);
|
||||
}
|
||||
else
|
||||
{
|
||||
resData.setPos(0);
|
||||
QMetaObject::invokeMethod(_printer, "printRequestFinished", Qt::QueuedConnection,
|
||||
Q_ARG(CURLcode, res),
|
||||
Q_ARG(Bytestream, resData));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(_printer, "printRequestFinished", Qt::QueuedConnection,
|
||||
Q_ARG(CURLcode, res),
|
||||
Q_ARG(Bytestream, resData));
|
||||
}
|
||||
}
|
||||
|
||||
void PrinterWorker::print(QString filename, QString mimeType, QString targetFormat, IppMsg job, PrintParameters Params, QMargins margins)
|
||||
{
|
||||
try
|
||||
{
|
||||
Mimer* mimer = Mimer::instance();
|
||||
|
||||
Bytestream contents = job.encode();
|
||||
|
||||
emit busyMessage(tr("Preparing"));
|
||||
|
||||
if((mimeType == targetFormat) && (targetFormat == Mimer::Postscript))
|
||||
{ // Can't process Postscript
|
||||
justUpload(filename, contents);
|
||||
}
|
||||
else if((mimeType == targetFormat) && (targetFormat == Mimer::Plaintext))
|
||||
{
|
||||
fixupPlaintext(filename, contents);
|
||||
}
|
||||
else if((mimeType != Mimer::SVG) && mimer->isImage(mimeType) && mimer->isImage(targetFormat))
|
||||
{ // Just make sure the image is in the desired format (and jpeg baseline-encoded), don't resize locally
|
||||
printImageAsImage(filename, contents, targetFormat);
|
||||
}
|
||||
else if(Params.format != PrintParameters::Invalid) // Params.format can be trusted
|
||||
{
|
||||
if(mimeType == Mimer::PDF)
|
||||
{
|
||||
convertPdf(filename, contents, Params);
|
||||
}
|
||||
else if(mimeType == Mimer::Plaintext)
|
||||
{
|
||||
if(Params.paperSizeH == 0 && Params.getPaperSizeWInMillimeters() < 26)
|
||||
{
|
||||
convertPlaintextLabel(filename, contents, Params);
|
||||
}
|
||||
else
|
||||
{
|
||||
convertPlaintext(filename, contents, Params);
|
||||
}
|
||||
}
|
||||
else if(Mimer::isImage(mimeType))
|
||||
{
|
||||
convertImage(filename, contents, Params, margins);
|
||||
}
|
||||
else if(Mimer::isOffice(mimeType))
|
||||
{
|
||||
convertOfficeDocument(filename, contents, Params);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit failed(tr("Cannot convert this file format"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
emit failed(tr("Cannot convert this file format"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
catch(const ConvertFailedException& e)
|
||||
{
|
||||
emit failed(e.what() == QString("") ? tr("Print error") : e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void PrinterWorker::justUpload(QString filename, Bytestream header)
|
||||
{
|
||||
emit busyMessage(tr("Printing"));
|
||||
|
||||
CurlRequester cr(_url);
|
||||
|
||||
QFile file(filename);
|
||||
file.open(QFile::ReadOnly);
|
||||
|
||||
OK(cr.write((char*)header.raw(), header.size()));
|
||||
QByteArray tmp = file.readAll();
|
||||
OK(cr.write(tmp.data(), tmp.length()));
|
||||
file.close();
|
||||
|
||||
awaitResult(cr, "printRequestFinished");
|
||||
}
|
||||
|
||||
void PrinterWorker::printImageAsImage(QString filename, Bytestream header, QString targetFormat)
|
||||
{
|
||||
QString imageFormat = "";
|
||||
QStringList supportedImageFormats = {Mimer::JPEG, Mimer::PNG};
|
||||
|
||||
|
||||
if(targetFormat == Mimer::RBMP)
|
||||
{
|
||||
// ok
|
||||
}
|
||||
else if(supportedImageFormats.contains(targetFormat))
|
||||
{
|
||||
imageFormat = targetFormat.split("/")[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ConvertFailedException(tr("Unknown target format"));
|
||||
}
|
||||
|
||||
QString mimeType = Mimer::instance()->get_type(filename);
|
||||
Bytestream OutBts;
|
||||
|
||||
CurlRequester cr(_url);
|
||||
|
||||
if(mimeType == Mimer::JPEG && targetFormat == Mimer::JPEG)
|
||||
{
|
||||
std::ifstream ifs = std::ifstream(filename.toStdString(), std::ios::in | std::ios::binary);
|
||||
Bytestream InBts(ifs);
|
||||
|
||||
baselinify(InBts, OutBts);
|
||||
}
|
||||
else if(targetFormat == Mimer::RBMP)
|
||||
{
|
||||
QImageReader reader(filename);
|
||||
reader.setAutoTransform(true);
|
||||
QImage inImage = reader.read();
|
||||
QBuffer buf;
|
||||
|
||||
if(inImage.isNull())
|
||||
{
|
||||
qDebug() << "failed to load";
|
||||
throw ConvertFailedException(tr("Failed to load image"));
|
||||
}
|
||||
|
||||
// TODO: calculate paper width minus margins
|
||||
// (depends on understanding/parsing custom paper sizes)
|
||||
int width = 576;
|
||||
int height = inImage.height() * ((width*1.0)/inImage.width());
|
||||
inImage = inImage.scaled(width, height);
|
||||
inImage = inImage.convertToFormat(QImage::Format_Mono);
|
||||
inImage = inImage.transformed(QMatrix().scale(1,-1));
|
||||
buf.open(QIODevice::ReadWrite);
|
||||
inImage.save(&buf, "bmp");
|
||||
buf.seek(0);
|
||||
OutBts = Bytestream(buf.size());
|
||||
buf.read((char*)(OutBts.raw()), buf.size());
|
||||
}
|
||||
else if(targetFormat == mimeType)
|
||||
{
|
||||
std::ifstream ifs = std::ifstream(filename.toStdString(), std::ios::in | std::ios::binary);
|
||||
OutBts = Bytestream(ifs);
|
||||
}
|
||||
else
|
||||
{
|
||||
QImageReader reader(filename);
|
||||
reader.setAutoTransform(true);
|
||||
QImage inImage = reader.read();
|
||||
QBuffer buf;
|
||||
|
||||
if(inImage.isNull())
|
||||
{
|
||||
qDebug() << "failed to load";
|
||||
throw ConvertFailedException(tr("Failed to load image"));
|
||||
}
|
||||
|
||||
buf.open(QIODevice::ReadWrite);
|
||||
inImage.save(&buf, imageFormat.toStdString().c_str());
|
||||
buf.seek(0);
|
||||
OutBts = Bytestream(buf.size());
|
||||
buf.read((char*)(OutBts.raw()), buf.size());
|
||||
}
|
||||
|
||||
emit busyMessage(tr("Printing"));
|
||||
|
||||
OK(cr.write((char*)header.raw(), header.size()));
|
||||
OK(cr.write((char*)OutBts.raw(), OutBts.size()));
|
||||
|
||||
awaitResult(cr, "printRequestFinished");
|
||||
}
|
||||
|
||||
void PrinterWorker::fixupPlaintext(QString filename, Bytestream header)
|
||||
{
|
||||
CurlRequester cr(_url);
|
||||
|
||||
QFile inFile(filename);
|
||||
if(!inFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
throw ConvertFailedException(tr("Failed to open file"));
|
||||
}
|
||||
|
||||
QString allText = inFile.readAll();
|
||||
if(allText.startsWith("\f"))
|
||||
{
|
||||
allText.remove(0, 1);
|
||||
}
|
||||
|
||||
if(allText.endsWith("\f"))
|
||||
{
|
||||
allText.chop(1);
|
||||
}
|
||||
else if(allText.endsWith("\f\n"))
|
||||
{
|
||||
allText.chop(2);
|
||||
}
|
||||
|
||||
QStringList lines;
|
||||
|
||||
for(QString rnline : allText.split("\r\n"))
|
||||
{
|
||||
lines.append(rnline.split("\n"));
|
||||
}
|
||||
|
||||
QByteArray outData = lines.join("\r\n").toUtf8();
|
||||
|
||||
OK(cr.write((char*)header.raw(), header.size()));
|
||||
OK(cr.write(outData.data(), outData.length()));
|
||||
|
||||
awaitResult(cr, "printRequestFinished");
|
||||
}
|
||||
|
||||
void PrinterWorker::convertPdf(QString filename, Bytestream header, PrintParameters Params)
|
||||
{
|
||||
emit busyMessage(tr("Printing"));
|
||||
|
||||
CurlRequester cr(_url);
|
||||
|
||||
OK(cr.write((char*)header.raw(), header.size()));
|
||||
|
||||
write_fun WriteFun([&cr](unsigned char const* buf, unsigned int len) -> bool
|
||||
{
|
||||
if(len == 0)
|
||||
return true;
|
||||
return cr.write((const char*)buf, len);
|
||||
});
|
||||
|
||||
progress_fun ProgressFun([this](size_t page, size_t total) -> void
|
||||
{
|
||||
emit progress(page, total);
|
||||
});
|
||||
|
||||
bool verbose = QLoggingCategory::defaultCategory()->isDebugEnabled();
|
||||
|
||||
int res = pdf_to_printable(filename.toStdString(), WriteFun, Params, ProgressFun, verbose);
|
||||
|
||||
if(res != 0)
|
||||
{
|
||||
throw ConvertFailedException(tr("Conversion failed"));
|
||||
}
|
||||
|
||||
awaitResult(cr, "printRequestFinished");
|
||||
qDebug() << "Finished";
|
||||
}
|
||||
|
||||
void PrinterWorker::convertImage(QString filename, Bytestream header, PrintParameters Params, QMargins margins)
|
||||
{
|
||||
QString mimeType = Mimer::instance()->get_type(filename);
|
||||
|
||||
if(Params.format == PrintParameters::URF && (Params.hwResW != Params.hwResH))
|
||||
{ // URF only supports symmetric resolutions
|
||||
qDebug() << "Unsupported URF resolution";
|
||||
throw ConvertFailedException(tr("Unsupported resolution (dpi)"));
|
||||
}
|
||||
|
||||
qDebug() << "Size is" << Params.getPaperSizeWInPixels() << "x" << Params.getPaperSizeHInPixels();
|
||||
|
||||
int leftMarginPx = (margins.left()/2540.0)*Params.hwResW;
|
||||
int rightMarginPx = (margins.right()/2540.0)*Params.hwResW;
|
||||
int topMarginPx = (margins.top()/2540.0)*Params.hwResH;
|
||||
int bottomMarginPx = (margins.bottom()/2540.0)*Params.hwResH;
|
||||
|
||||
int totalXMarginPx = leftMarginPx+rightMarginPx;
|
||||
int totalYMarginPx = topMarginPx+bottomMarginPx;
|
||||
|
||||
size_t targetWidth = Params.getPaperSizeWInPixels()-totalXMarginPx;
|
||||
size_t targetHeight = Params.getPaperSizeHInPixels()-totalYMarginPx;
|
||||
|
||||
QImage inImage;
|
||||
if(mimeType == Mimer::SVG)
|
||||
{
|
||||
QSvgRenderer renderer(filename);
|
||||
if(!renderer.isValid())
|
||||
{
|
||||
qDebug() << "failed to load svg";
|
||||
throw ConvertFailedException(tr("Failed to load image"));
|
||||
}
|
||||
QSize defaultSize = renderer.defaultSize();
|
||||
QSize targetSize(targetWidth, targetHeight);
|
||||
|
||||
if(defaultSize.width() > defaultSize.height())
|
||||
{
|
||||
targetSize.transpose();
|
||||
}
|
||||
|
||||
QSize initialSize = defaultSize.scaled(targetSize, Qt::KeepAspectRatio);
|
||||
|
||||
inImage = QImage(initialSize, QImage::Format_RGB32);
|
||||
inImage.fill(QColor("white"));
|
||||
QPainter painter(&inImage);
|
||||
|
||||
renderer.render(&painter);
|
||||
painter.end(); // Or else the painter segfaults on destruction if we have messed with the image
|
||||
|
||||
if(inImage.width() > inImage.height())
|
||||
{
|
||||
inImage = inImage.transformed(QMatrix().rotate(270.0));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
QImageReader reader(filename);
|
||||
reader.setAutoTransform(true);
|
||||
inImage = reader.read();
|
||||
|
||||
if(inImage.isNull())
|
||||
{
|
||||
qDebug() << "failed to load";
|
||||
throw ConvertFailedException(tr("Failed to load image"));
|
||||
}
|
||||
if(inImage.width() > inImage.height())
|
||||
{
|
||||
inImage = inImage.transformed(QMatrix().rotate(270.0));
|
||||
}
|
||||
|
||||
inImage = inImage.scaled(targetWidth, targetHeight,
|
||||
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
|
||||
if(Params.format == PrintParameters::PDF || Params.format == PrintParameters::Postscript)
|
||||
{
|
||||
QTemporaryFile tmpPdfFile;
|
||||
tmpPdfFile.open();
|
||||
QPdfWriter pdfWriter(tmpPdfFile.fileName());
|
||||
pdfWriter.setCreator("SeaPrint " SEAPRINT_VERSION);
|
||||
QPageSize pageSize({Params.getPaperSizeWInPoints(), Params.getPaperSizeHInPoints()}, QPageSize::Point);
|
||||
pdfWriter.setPageSize(pageSize);
|
||||
pdfWriter.setResolution(Params.hwResH);
|
||||
QPainter painter(&pdfWriter);
|
||||
int xOffset = ((pdfWriter.width()-totalXMarginPx)-inImage.width())/2 + leftMarginPx;
|
||||
int yOffset = ((pdfWriter.height()-totalYMarginPx)-inImage.height())/2 + topMarginPx;
|
||||
painter.drawImage(xOffset, yOffset, inImage);
|
||||
painter.end();
|
||||
|
||||
convertPdf(tmpPdfFile.fileName(), header, Params);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t total_pages = Params.documentCopies*Params.pageCopies;
|
||||
|
||||
if(total_pages > 1 && Params.duplex)
|
||||
{ // Images are one page by definition - if we need to do client-side copies, they must be one-sided or we'd have to insert backsides
|
||||
qDebug() << "Inconsistent duplex setting";
|
||||
throw ConvertFailedException(tr("Inconsistent duplex setting"));
|
||||
}
|
||||
|
||||
QImage outImage = QImage(Params.getPaperSizeWInPixels(), Params.getPaperSizeHInPixels(), inImage.format());
|
||||
outImage.fill(Qt::white);
|
||||
QPainter painter(&outImage);
|
||||
int xOffset = ((outImage.width()-totalXMarginPx)-inImage.width())/2 + leftMarginPx;
|
||||
int yOffset = ((outImage.height()-totalYMarginPx)-inImage.height())/2 + topMarginPx;
|
||||
painter.drawImage(xOffset, yOffset, inImage);
|
||||
painter.end();
|
||||
|
||||
QBuffer buf;
|
||||
buf.open(QIODevice::ReadWrite);
|
||||
Bytestream fileHdr, outBts;
|
||||
|
||||
|
||||
if(inImage.allGray())
|
||||
{
|
||||
Params.colors = 1; // No need to waste space/bandwidth...
|
||||
}
|
||||
|
||||
outImage.save(&buf, Params.colors==1 ? "pgm" : "ppm");
|
||||
buf.seek(0);
|
||||
// Skip header - TODO consider reimplementing
|
||||
buf.readLine(255);
|
||||
buf.readLine(255);
|
||||
buf.readLine(255);
|
||||
|
||||
Bytestream inBts(Params.getPaperSizeWInPixels() * Params.getPaperSizeHInPixels() * Params.colors);
|
||||
|
||||
if((((size_t)buf.size())-buf.pos()) != inBts.size())
|
||||
{
|
||||
qDebug() << buf.size() << buf.pos() << inBts.size();
|
||||
throw ConvertFailedException();
|
||||
}
|
||||
|
||||
buf.read((char*)(inBts.raw()), inBts.size());
|
||||
|
||||
fileHdr << (Params.format == PrintParameters::URF ? make_urf_file_hdr(1) : make_pwg_file_hdr());
|
||||
|
||||
bool verbose = QLoggingCategory::defaultCategory()->isDebugEnabled();
|
||||
|
||||
bmp_to_pwg(inBts, outBts, 1, Params, verbose);
|
||||
|
||||
CurlRequester cr(_url);
|
||||
|
||||
emit busyMessage(tr("Printing"));
|
||||
|
||||
OK(cr.write((char*)header.raw(), header.size()));
|
||||
OK(cr.write((char*)fileHdr.raw(), fileHdr.size()));
|
||||
for(size_t c = 0; c < total_pages; c++)
|
||||
{
|
||||
OK(cr.write((char*)(outBts.raw()), outBts.size()));
|
||||
emit progress(c+1, total_pages);
|
||||
}
|
||||
|
||||
awaitResult(cr, "printRequestFinished");
|
||||
}
|
||||
|
||||
qDebug() << "posted";
|
||||
}
|
||||
|
||||
void PrinterWorker::convertOfficeDocument(QString filename, Bytestream header, PrintParameters Params)
|
||||
{
|
||||
if(Params.format == PrintParameters::URF && (Params.hwResW != Params.hwResH))
|
||||
{ // URF only supports symmetric resolutions
|
||||
qDebug() << "Unsupported URF resolution";
|
||||
throw ConvertFailedException(tr("Unsupported resolution (dpi)"));
|
||||
}
|
||||
|
||||
QString ShortPaperSize;
|
||||
if(CalligraPaperSizes.contains(Params.paperSizeName.c_str()))
|
||||
{
|
||||
ShortPaperSize = CalligraPaperSizes[Params.paperSizeName.c_str()];
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Unsupported PDF paper size" << Params.paperSizeName.c_str();
|
||||
throw ConvertFailedException(tr("Unsupported PDF paper size"));
|
||||
}
|
||||
|
||||
QProcess CalligraConverter(this);
|
||||
CalligraConverter.setProgram("calligraconverter");
|
||||
QStringList CalligraConverterArgs = {"--batch", "--mimetype", Mimer::PDF, "--print-orientation", "Portrait", "--print-papersize", ShortPaperSize};
|
||||
|
||||
CalligraConverterArgs << filename;
|
||||
|
||||
QTemporaryFile tmpPdfFile;
|
||||
tmpPdfFile.open();
|
||||
CalligraConverterArgs << tmpPdfFile.fileName();
|
||||
|
||||
qDebug() << "CalligraConverteArgs is" << CalligraConverterArgs;
|
||||
CalligraConverter.setArguments(CalligraConverterArgs);
|
||||
|
||||
CalligraConverter.start();
|
||||
|
||||
qDebug() << "CalligraConverter Starting";
|
||||
|
||||
if(!CalligraConverter.waitForStarted())
|
||||
{
|
||||
qDebug() << "CalligraConverter died";
|
||||
throw ConvertFailedException();
|
||||
}
|
||||
|
||||
qDebug() << "CalligraConverter Started";
|
||||
|
||||
if(!CalligraConverter.waitForFinished(-1))
|
||||
{
|
||||
qDebug() << "CalligraConverter failed";
|
||||
throw ConvertFailedException();
|
||||
}
|
||||
|
||||
// qDebug() << CalligraConverter->readAllStandardError();
|
||||
|
||||
convertPdf(tmpPdfFile.fileName(), header, Params);
|
||||
|
||||
qDebug() << "posted";
|
||||
}
|
||||
|
||||
void PrinterWorker::convertPlaintext(QString filename, Bytestream header, PrintParameters Params)
|
||||
{
|
||||
|
||||
QPageSize pageSize;
|
||||
|
||||
if(Params.paperSizeUnits == PrintParameters::Millimeters)
|
||||
{
|
||||
pageSize = QPageSize(QSizeF {Params.paperSizeW, Params.paperSizeH}, QPageSize::Millimeter);
|
||||
}
|
||||
else if(Params.paperSizeUnits == PrintParameters::Inches)
|
||||
{
|
||||
pageSize = QPageSize(QSizeF {Params.paperSizeW, Params.paperSizeH}, QPageSize::Inch);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Unsupported paper size" << Params.paperSizeName.c_str();
|
||||
throw ConvertFailedException(tr("Unsupported paper size"));
|
||||
}
|
||||
|
||||
QFile inFile(filename);
|
||||
if(!inFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
throw ConvertFailedException(tr("Failed to open file"));
|
||||
}
|
||||
|
||||
quint32 resolution = std::min(Params.hwResW, Params.hwResH);
|
||||
|
||||
QTemporaryFile tmpPdfFile;
|
||||
tmpPdfFile.open();
|
||||
|
||||
QPdfWriter pdfWriter(tmpPdfFile.fileName());
|
||||
pdfWriter.setCreator("SeaPrint " SEAPRINT_VERSION);
|
||||
|
||||
pdfWriter.setPageSize(pageSize);
|
||||
pdfWriter.setResolution(resolution);
|
||||
|
||||
qreal docHeight = pageSize.sizePixels(resolution).height();
|
||||
|
||||
QTextDocument doc;
|
||||
|
||||
QFont font = QFont("Courier");
|
||||
font.setPointSizeF(1);
|
||||
|
||||
qreal charHeight = 0;
|
||||
|
||||
// Find the optimal font size
|
||||
while(true) {
|
||||
QFont tmpFont = font;
|
||||
tmpFont.setPointSizeF(font.pointSizeF()+0.5);
|
||||
QFontMetricsF qfm(tmpFont, &pdfWriter);
|
||||
|
||||
charHeight = qfm.lineSpacing();
|
||||
|
||||
if(charHeight*66 > docHeight)
|
||||
{
|
||||
break;
|
||||
}
|
||||
font=tmpFont;
|
||||
}
|
||||
|
||||
QFontMetricsF qfm(font, &pdfWriter);
|
||||
|
||||
charHeight = qfm.height();
|
||||
|
||||
int textHeight = 60*charHeight;
|
||||
qreal margin = ((docHeight-textHeight)/2);
|
||||
qreal mmMargin = margin/(resolution/25.4);
|
||||
|
||||
doc.setDefaultFont(font);
|
||||
(void)doc.documentLayout(); // wat
|
||||
|
||||
// Needs to be before painter
|
||||
pdfWriter.setMargins({mmMargin, mmMargin, mmMargin, mmMargin});
|
||||
|
||||
QPainter painter(&pdfWriter);
|
||||
|
||||
doc.documentLayout()->setPaintDevice(painter.device());
|
||||
doc.setDocumentMargin(margin);
|
||||
|
||||
// Hack to make the document and pdfWriter margins overlap
|
||||
// Apparently calls to painter.translate() stack... who knew!
|
||||
painter.translate(-margin, -margin);
|
||||
|
||||
QRectF body = pageSize.rectPixels(resolution);
|
||||
doc.setPageSize(body.size());
|
||||
|
||||
QString allText = inFile.readAll();
|
||||
if(allText.startsWith("\f"))
|
||||
{
|
||||
allText.remove(0, 1);
|
||||
}
|
||||
|
||||
if(allText.endsWith("\f"))
|
||||
{
|
||||
allText.chop(1);
|
||||
}
|
||||
else if(allText.endsWith("\f\n"))
|
||||
{
|
||||
allText.chop(2);
|
||||
}
|
||||
|
||||
QStringList pages = allText.split('\f');
|
||||
|
||||
bool first = true;
|
||||
int pageCount = 0;
|
||||
|
||||
for(QString page : pages)
|
||||
{
|
||||
if(!first)
|
||||
{
|
||||
pdfWriter.newPage();
|
||||
}
|
||||
first = false;
|
||||
|
||||
if(page.endsWith("\n"))
|
||||
{
|
||||
page.chop(1);
|
||||
}
|
||||
doc.setPlainText(page);
|
||||
|
||||
int p = 0; // Page number in this document, starting from 0
|
||||
|
||||
while(true)
|
||||
{
|
||||
painter.save();
|
||||
painter.translate(body.left(), body.top() - p*body.height());
|
||||
QRectF view(0, p*body.height(), body.width(), body.height());
|
||||
painter.setClipRect(view);
|
||||
|
||||
QAbstractTextDocumentLayout::PaintContext context;
|
||||
context.clip = view;
|
||||
context.palette.setColor(QPalette::Text, Qt::black);
|
||||
doc.documentLayout()->draw(&painter, context);
|
||||
painter.restore();
|
||||
|
||||
p++;
|
||||
pageCount++;
|
||||
|
||||
if(p >= doc.pageCount())
|
||||
break;
|
||||
|
||||
pdfWriter.newPage();
|
||||
}
|
||||
}
|
||||
|
||||
painter.end();
|
||||
|
||||
convertPdf(tmpPdfFile.fileName(), header, Params);
|
||||
|
||||
qDebug() << "Finished";
|
||||
qDebug() << "posted";
|
||||
}
|
||||
|
||||
void PrinterWorker::convertPlaintextLabel(QString filename, Bytestream header, PrintParameters Params)
|
||||
{
|
||||
QFile inFile(filename);
|
||||
if(!inFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
throw ConvertFailedException(tr("Failed to open file"));
|
||||
}
|
||||
|
||||
QString allText = inFile.readAll();
|
||||
while(allText.endsWith('\n'))
|
||||
{
|
||||
allText.chop(1);
|
||||
}
|
||||
|
||||
if(allText.contains('\n') || allText.contains('\f'))
|
||||
{
|
||||
throw ConvertFailedException(tr("Multiline label not supported"));
|
||||
}
|
||||
|
||||
QTemporaryFile tmpPdfFile;
|
||||
tmpPdfFile.open();
|
||||
|
||||
// NB: running with rotated format - pdf2printable will sort that out
|
||||
int pixelHeight = Params.getPaperSizeWInPixels();
|
||||
QFont font = QFont("Arial");
|
||||
font.setPixelSize(pixelHeight);
|
||||
|
||||
QFontMetrics fm(font);
|
||||
QString bigText = "AXgjqÈÅÄÖþ";
|
||||
QRect rect = fm.boundingRect(bigText);
|
||||
|
||||
while(rect.height() > pixelHeight*(8.0/10.0))
|
||||
{
|
||||
font.setPixelSize(font.pixelSize()-1);
|
||||
fm = QFontMetrics(font);
|
||||
rect = fm.boundingRect(bigText);
|
||||
}
|
||||
|
||||
rect = fm.boundingRect(allText);
|
||||
|
||||
int pixelWidth = rect.width() + pixelHeight/2;
|
||||
float ratio = pixelWidth*1.0/pixelHeight;
|
||||
Params.paperSizeH = Params.paperSizeW * ratio;
|
||||
|
||||
QPdfWriter pdfWriter(tmpPdfFile.fileName());
|
||||
pdfWriter.setCreator("SeaPrint " SEAPRINT_VERSION);
|
||||
QPageSize pageSize({Params.getPaperSizeHInPoints(), Params.getPaperSizeWInPoints()}, QPageSize::Point);
|
||||
pdfWriter.setPageSize(pageSize);
|
||||
pdfWriter.setMargins({0,0,0,0});
|
||||
pdfWriter.setResolution(Params.hwResH);
|
||||
|
||||
QPainter painter(&pdfWriter);
|
||||
int xOffset = -rect.x() + pixelHeight/4;
|
||||
int yOffset = -rect.y() + pixelHeight/10;
|
||||
painter.setFont(font);
|
||||
painter.drawText(xOffset, yOffset, allText);
|
||||
painter.end();
|
||||
|
||||
convertPdf(tmpPdfFile.fileName(), header, Params);
|
||||
|
||||
qDebug() << "Finished";
|
||||
qDebug() << "posted";
|
||||
}
|
||||
|
||||
void PrinterWorker::awaitResult(CurlRequester& cr, QString callback)
|
||||
{
|
||||
Bytestream resMsg;
|
||||
CURLcode res = cr.await(&resMsg);
|
||||
QMetaObject::invokeMethod(_printer, callback.toStdString().c_str(), Qt::QueuedConnection,
|
||||
Q_ARG(CURLcode, res),
|
||||
Q_ARG(Bytestream, resMsg));
|
||||
}
|
72
src/printerworker.h
Normal file
72
src/printerworker.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
#ifndef PRINTERWORKER_H
|
||||
#define PRINTERWORKER_H
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
#include "curlrequester.h"
|
||||
#include "ippmsg.h"
|
||||
#include "ppm2pwg/printparameters.h"
|
||||
|
||||
class IppPrinter;
|
||||
|
||||
class ConvertFailedException: public std::exception
|
||||
{
|
||||
private:
|
||||
std::string _msg;
|
||||
public:
|
||||
ConvertFailedException(QString msg = "") : _msg(msg.toStdString())
|
||||
{
|
||||
}
|
||||
virtual const char* what() const throw()
|
||||
{
|
||||
return _msg.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class PrinterWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PrinterWorker(IppPrinter* parent);
|
||||
~PrinterWorker();
|
||||
|
||||
private:
|
||||
PrinterWorker();
|
||||
|
||||
public slots:
|
||||
void urlChanged(QUrl url);
|
||||
void getStrings(QUrl url);
|
||||
void getImage(QUrl url);
|
||||
void getPrinterAttributes(Bytestream msg);
|
||||
void getJobs(Bytestream msg);
|
||||
void cancelJob(Bytestream msg);
|
||||
void identify(Bytestream msg);
|
||||
void print(QString filename, QString mimeType, QString targetFormat, IppMsg job, PrintParameters Params, QMargins margins);
|
||||
void print2(QString filename, QString mimeType, QString targetFormat, IppMsg createJob, IppMsg sendDocument, PrintParameters Params, QMargins margins);
|
||||
|
||||
private slots:
|
||||
void cleanup();
|
||||
|
||||
signals:
|
||||
void progress(qint64 done, qint64 pages);
|
||||
void busyMessage(QString msg);
|
||||
void failed(QString message);
|
||||
|
||||
private:
|
||||
void justUpload(QString filename, Bytestream header);
|
||||
void printImageAsImage(QString filename, Bytestream header, QString targetFormat);
|
||||
void fixupPlaintext(QString filename, Bytestream header);
|
||||
void convertPdf(QString filename, Bytestream header, PrintParameters Params);
|
||||
void convertImage(QString filename, Bytestream header, PrintParameters Params, QMargins margins);
|
||||
void convertOfficeDocument(QString filename, Bytestream header, PrintParameters Params);
|
||||
void convertPlaintext(QString filename, Bytestream header, PrintParameters Params);
|
||||
void convertPlaintextLabel(QString filename, Bytestream header, PrintParameters Params);
|
||||
|
||||
void awaitResult(CurlRequester& cr, QString callback);
|
||||
|
||||
std::unique_ptr<QThread> _thread;
|
||||
QPointer<IppPrinter> _printer;
|
||||
QUrl _url;
|
||||
};
|
||||
|
||||
#endif // PRINTERWORKER_H
|
47
src/rangelistchecker.cpp
Normal file
47
src/rangelistchecker.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "rangelistchecker.h"
|
||||
|
||||
RangeListChecker::RangeListChecker()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
RangeListChecker::~RangeListChecker()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
RangeListChecker* RangeListChecker::m_Instance = 0;
|
||||
|
||||
RangeListChecker* RangeListChecker::instance()
|
||||
{
|
||||
static QMutex mutex;
|
||||
if (!m_Instance)
|
||||
{
|
||||
mutex.lock();
|
||||
|
||||
if (!m_Instance)
|
||||
m_Instance = new RangeListChecker;
|
||||
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
return m_Instance;
|
||||
}
|
||||
|
||||
QJsonArray RangeListChecker::parse(QString str) const
|
||||
{
|
||||
PrintParameters params;
|
||||
params.setPageRange(str.toStdString());
|
||||
QJsonArray ret;
|
||||
size_t previous = 0;
|
||||
for(const std::pair<size_t, size_t>& p : params.pageRangeList)
|
||||
{
|
||||
if(p.first <= previous)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
ret.append(QJsonObject {{"low", int(p.first)}, {"high", int(p.second)}});
|
||||
previous = p.second;
|
||||
}
|
||||
return ret;
|
||||
}
|
29
src/rangelistchecker.h
Normal file
29
src/rangelistchecker.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef RANGELISTCHECKER_H
|
||||
#define RANGELISTCHECKER_H
|
||||
|
||||
#include <QValidator>
|
||||
#include <QMutex>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "printparameters.h"
|
||||
|
||||
class RangeListChecker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static RangeListChecker* instance();
|
||||
|
||||
Q_INVOKABLE QJsonArray parse(QString str) const;
|
||||
|
||||
private:
|
||||
static RangeListChecker* m_Instance;
|
||||
|
||||
RangeListChecker();
|
||||
~RangeListChecker();
|
||||
RangeListChecker(const RangeListChecker &);
|
||||
RangeListChecker& operator=(const RangeListChecker &);
|
||||
|
||||
};
|
||||
|
||||
#endif // RANGELISTCHECKER_H
|
21
src/seaprintdbusadaptor.cpp
Normal file
21
src/seaprintdbusadaptor.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include "seaprintdbusadaptor.h"
|
||||
#include <QtQuick>
|
||||
|
||||
SeaPrintDBusAdaptor::SeaPrintDBusAdaptor(QQuickView *view)
|
||||
: QDBusAbstractAdaptor(view)
|
||||
, DBusAdaptorBase(view)
|
||||
{
|
||||
}
|
||||
|
||||
SeaPrintDBusAdaptor::~SeaPrintDBusAdaptor()
|
||||
{
|
||||
}
|
||||
|
||||
void SeaPrintDBusAdaptor::Open(const QString& fileName)
|
||||
{
|
||||
doOpen(fileName);
|
||||
}
|
||||
void SeaPrintDBusAdaptor::OpenPlaintext(const QString& fileContents, const QString& name)
|
||||
{
|
||||
doCreateFileAndOpen(fileContents, name);
|
||||
}
|
21
src/seaprintdbusadaptor.h
Normal file
21
src/seaprintdbusadaptor.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef SEAPRINTDBUSADAPTOR_H
|
||||
#define SEAPRINTDBUSADAPTOR_H
|
||||
|
||||
#include "dbusadaptorbase.h"
|
||||
|
||||
class SeaPrintDBusAdaptor : public QDBusAbstractAdaptor, public DBusAdaptorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "net.attah.seaprint")
|
||||
|
||||
public:
|
||||
SeaPrintDBusAdaptor(QQuickView *view);
|
||||
~SeaPrintDBusAdaptor();
|
||||
|
||||
public Q_SLOTS:
|
||||
Q_NOREPLY void Open(const QString& fileName);
|
||||
Q_NOREPLY void OpenPlaintext(const QString& fileContents, const QString& name);
|
||||
|
||||
};
|
||||
|
||||
#endif // SEAPRINTDBUSADAPTOR_H
|
|
@ -2,7 +2,9 @@
|
|||
#include <QMutex>
|
||||
|
||||
|
||||
Settings::Settings() : _ignoreSslErrorsSetting("/apps/harbour-seaprint/settings/ignore-ssl-errors", this)
|
||||
Settings::Settings() : _ignoreSslErrorsSetting("/apps/harbour-seaprint/settings/ignore-ssl-errors", this),
|
||||
_debugLogSetting("/apps/harbour-seaprint/settings/debug-log", this),
|
||||
_allowExternalConnectionsSetting("/apps/harbour-seaprint/settings/allow-external-connections", this)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -37,3 +39,23 @@ QString Settings::ignoreSslErrorsPath()
|
|||
{
|
||||
return _ignoreSslErrorsSetting.key();
|
||||
}
|
||||
|
||||
bool Settings::debugLog()
|
||||
{
|
||||
return _debugLogSetting.value(_debugLogDefault).toBool();
|
||||
}
|
||||
|
||||
QString Settings::debugLogPath()
|
||||
{
|
||||
return _debugLogSetting.key();
|
||||
}
|
||||
|
||||
bool Settings::allowExternalConnections()
|
||||
{
|
||||
return _allowExternalConnectionsSetting.value(_allowExternalConnectionsDefault).toBool();
|
||||
}
|
||||
|
||||
QString Settings::allowExternalConnectionsPath()
|
||||
{
|
||||
return _allowExternalConnectionsSetting.key();
|
||||
}
|
||||
|
|
|
@ -11,10 +11,18 @@ public:
|
|||
Q_PROPERTY(QString ignoreSslErrorsPath READ ignoreSslErrorsPath CONSTANT)
|
||||
Q_PROPERTY(bool ignoreSslErrorsDefault MEMBER _ignoreSslErrorsDefault CONSTANT)
|
||||
|
||||
Q_PROPERTY(QString debugLogPath READ debugLogPath CONSTANT)
|
||||
Q_PROPERTY(bool debugLogDefault MEMBER _debugLogDefault CONSTANT)
|
||||
|
||||
Q_PROPERTY(QString allowExternalConnectionsPath READ allowExternalConnectionsPath CONSTANT)
|
||||
Q_PROPERTY(bool allowExternalConnectionsDefault MEMBER _allowExternalConnectionsDefault CONSTANT)
|
||||
|
||||
public:
|
||||
static Settings* instance();
|
||||
|
||||
bool ignoreSslErrors();
|
||||
bool debugLog();
|
||||
bool allowExternalConnections();
|
||||
|
||||
private:
|
||||
Settings();
|
||||
|
@ -25,10 +33,17 @@ private:
|
|||
static Settings* m_Instance;
|
||||
|
||||
MGConfItem _ignoreSslErrorsSetting;
|
||||
MGConfItem _debugLogSetting;
|
||||
MGConfItem _allowExternalConnectionsSetting;
|
||||
|
||||
QString ignoreSslErrorsPath();
|
||||
QString debugLogPath();
|
||||
QString allowExternalConnectionsPath();
|
||||
|
||||
bool _ignoreSslErrorsDefault = true;
|
||||
bool _debugLogDefault = false;
|
||||
bool _allowExternalConnectionsDefault = false;
|
||||
|
||||
};
|
||||
|
||||
#endif // SETTINGS_H
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
#include "svgprovider.h"
|
||||
|
||||
|
||||
SvgProvider::SvgProvider() : QQuickImageProvider(QQuickImageProvider::Image, ForceAsynchronousImageLoading)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SvgProvider::~SvgProvider() {
|
||||
}
|
||||
|
||||
SvgProvider* SvgProvider::m_Instance = nullptr;
|
||||
|
||||
SvgProvider* SvgProvider::instance()
|
||||
{
|
||||
static QMutex mutex;
|
||||
if (!m_Instance)
|
||||
{
|
||||
mutex.lock();
|
||||
|
||||
if (!m_Instance)
|
||||
m_Instance = new SvgProvider;
|
||||
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
return m_Instance;
|
||||
}
|
||||
|
||||
QImage SvgProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
|
||||
{
|
||||
QSvgRenderer renderer(SailfishApp::pathTo(id).toLocalFile());
|
||||
*size = requestedSize.isValid() ? requestedSize : renderer.defaultSize();
|
||||
|
||||
QImage img(*size, QImage::Format_ARGB32);
|
||||
img.fill(QColor("transparent"));
|
||||
QPainter painter(&img);
|
||||
qDebug() << img << painter.isActive();
|
||||
|
||||
renderer.render(&painter);
|
||||
|
||||
return img;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#ifndef SVGPROVIDER_H
|
||||
#define SVGPROVIDER_H
|
||||
|
||||
#include <QQuickImageProvider>
|
||||
#include <QPainter>
|
||||
#include <QtSvg/QSvgRenderer>
|
||||
#include <QMutex>
|
||||
#include <QtDebug>
|
||||
#include <sailfishapp.h>
|
||||
|
||||
class SvgProvider : public QQuickImageProvider
|
||||
{
|
||||
public:
|
||||
static SvgProvider* instance();
|
||||
|
||||
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
|
||||
|
||||
private:
|
||||
SvgProvider();
|
||||
~SvgProvider();
|
||||
SvgProvider(const SvgProvider &);
|
||||
SvgProvider& operator=(const SvgProvider &);
|
||||
|
||||
static SvgProvider* m_Instance;
|
||||
|
||||
};
|
||||
|
||||
#endif // SVGPROVIDER_H
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue