Compare commits

..

1 commit

Author SHA1 Message Date
Anton Thomasson
547da9d145 Disable sailjail for 4.3 2022-01-04 19:39:15 +01:00
70 changed files with 4432 additions and 38209 deletions

View file

@ -11,7 +11,7 @@ A minority of printers support only PCL and/or their own proprietary formats, th
## Format support
For PDFs, your printer needs to support one of the following formats:
For PDFs or plaintext, your printer needs to support one of the following formats:
* PDF
* Postscript
* PWG-raster
@ -19,9 +19,6 @@ For PDFs, your printer needs to support one of the following formats:
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.
@ -34,18 +31,8 @@ For images, your printer needs to support one of the following formats:
* PDF
* Postscript
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).
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.
Printers with any of these IPP certifications and derivative standards should likely be supported:

View file

@ -1,27 +0,0 @@
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)

View file

@ -1,444 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 20 KiB

View file

@ -3,10 +3,9 @@ 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;RemovableMedia;MediaIndexing;Compatibility
OrganizationName=net.attah
ApplicationName=seaprint
ExecDBus=harbour-seaprint --prestart
#[X-Sailjail]
#Permissions=Internet;Pictures;Documents;Downloads;MediaIndexing;Compatibility
#OrganizationName=net.attah
#ApplicationName=seaprint

View file

@ -26,36 +26,31 @@ INSTALLS += i18n
system(lrelease $$PWD/translations/*.ts)
CONFIG += sailfishapp
QT += svg dbus
PKGCONFIG += mlite5 libcurl glib-2.0
INCLUDEPATH += $$system(pkg-config --cflags-only-I poppler-glib | sed 's/-I//g')
QT += svg
PKGCONFIG += mlite5 libcurl poppler glib-2.0 cairo
LIBS += -lcurl -lglib-2.0 -lgobject-2.0 -ldl
DEFINES += MADNESS=1
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/imageitem.cpp \
src/curlworker.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/printerworker.cpp \
src/seaprintdbusadaptor.cpp \
src/settings.cpp
src/settings.cpp \
src/svgprovider.cpp
DISTFILES += qml/harbour-seaprint.qml \
qml/components/DependentOn.qml \
qml/cover/CoverPage.qml \
qml/components/*qml \
qml/pages/*.qml \
@ -86,33 +81,27 @@ 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/imageitem.h \
src/curlworker.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/printerworker.h \
src/seaprintdbusadaptor.h \
src/settings.h
src/settings.h \
src/svgprovider.h
INCLUDEPATH += ppm2pwg \
ppm2pwg/bytestream

@ -1 +1 @@
Subproject commit acc32f3769c3b18bd6b94c55b882c7b7b778854c
Subproject commit aa53021c0cdcd6594a8d54955ceccd45e68f6566

View file

@ -7,34 +7,33 @@ Setting {
property var preferred_choices: []
property string preferred_choice_suffix: ""
property var actual_choices: Utils.fixupChoices(name, choices, parent.selectedFileType, parent.printer)
property var actual_choices: Utils.fixupChoices(name, choices, parent.selectedFileType)
property int num_large_choices: 8
displayValue: Utils.ippName(name, choice != undefined ? choice : default_choice, strings)
displayValue: Utils.ippName(name, choice != undefined ? choice : default_choice)
onClicked: {
if(hasMenu)
{
menu.open(this)
}
else if(actual_choices.length != 0)
if(actual_choices.length>num_large_choices)
{
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,
strings: strings})
preferred_choices: preferred_choices, preferred_choice_suffix: preferred_choice_suffix})
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], strings)
text: Utils.ippName(name, actual_choices[index])
+ (Utils.has(preferred_choices, actual_choices[index]) ? " "+preferred_choice_suffix : "")
onClicked:
{
@ -45,5 +44,7 @@ Setting {
}
hasMenu: actual_choices.length != 0 && (actual_choices.length <= num_large_choices)
hasMenu: !actual_choices.length>num_large_choices
}

View file

@ -71,34 +71,4 @@ 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"
}
}

View file

@ -11,6 +11,12 @@ Dialog {
property string title
canAccept: valueField.acceptableInput
Component.onCompleted:
{
console.log("adasdsa", title)
}
Column {
width: parent.width
@ -20,9 +26,10 @@ Dialog {
id: valueField
validator: IntValidator{bottom: min; top: max;}
width: parent.width
placeholderText: ""+min+"-"+(max == 65535 ? "..." : max)
placeholderText: ""+min+"-"+max
label: title
focus: true
labelVisible: true
inputMethodHints: Qt.ImhDigitsOnly
}

View file

@ -2,21 +2,8 @@ import QtQuick 2.6
import Sailfish.Silica 1.0
Setting {
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 int low: valid ? parent.printer.attrs[name+"-supported"].value.low : 0
property int high: valid ? parent.printer.attrs[name+"-supported"].value.high : 0
property bool suppressChange: false
@ -25,6 +12,7 @@ Setting {
onChoiceChanged: {
if(choice == undefined)
{
console.log("choice unset");
suppressChange = true;
slider.value = slider.minimumValue;
suppressChange = false;
@ -53,7 +41,7 @@ Setting {
anchors.right: parent.right
icon.source: "image://theme/icon-s-edit"
onClicked: {var dialog = pageStack.push(Qt.resolvedUrl("IntegerInputDialog.qml"),
{title: prettyName,
{value: choice, title: prettyName,
min: low, max: high});
dialog.accepted.connect(function() {
choice = dialog.value;

View file

@ -12,7 +12,6 @@ Dialog {
property var choices
property var preferred_choices
property string preferred_choice_suffix
property var strings
canAccept: false
SilicaListView
@ -31,7 +30,7 @@ Dialog {
x: Theme.paddingLarge
anchors.verticalCenter: parent.verticalCenter
highlighted: choices[index]==new_choice
text: Utils.ippName(name, choices[index], strings)
text: Utils.ippName(name, choices[index])
+ (Utils.has(preferred_choices, choices[index]) ? " "+preferred_choice_suffix : "")
}
}

View file

@ -1,30 +0,0 @@
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) }
}
}
}

View file

@ -5,7 +5,6 @@ Setting {
property int high
property int choice_low: 1
property int choice_high: 0
property bool acceptRangeList: false
property bool suppressChange: false
@ -42,8 +41,9 @@ Setting {
}
onChoiceChanged: {
if(choice == undefined || choice.constructor.name !== "Object")
if(choice == undefined)
{
console.log("choice unset");
suppressChange = true;
low_slider.value = low_slider.minimumValue;
high_slider.value = high_slider.minimumValue;
@ -51,39 +51,7 @@ Setting {
}
}
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
}
}
displayValue: choice == undefined ? qsTr("all") : ""+choice.low+" - "+choice.high
menu: ContextMenu {
MenuItem {
@ -104,7 +72,7 @@ Setting {
anchors.right: parent.right
icon.source: "image://theme/icon-s-edit"
onClicked: {var dialog = pageStack.push(Qt.resolvedUrl("IntegerInputDialog.qml"),
{title: prettyName + " (" + qsTr("Low") + ")",
{value: choice, title: prettyName,
min: 1, max: high});
dialog.accepted.connect(function() {
choice_low = dialog.value;
@ -132,7 +100,7 @@ Setting {
anchors.right: parent.right
icon.source: "image://theme/icon-s-edit"
onClicked: {var dialog = pageStack.push(Qt.resolvedUrl("IntegerInputDialog.qml"),
{title: prettyName + " (" + qsTr("High") + ")",
{value: choice, title: prettyName,
min: 1, max: high});
dialog.accepted.connect(function() {
choice_high = dialog.value;
@ -142,17 +110,6 @@ 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;
})
}
}
}

View file

@ -15,8 +15,6 @@ 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)

View file

@ -84,6 +84,6 @@ Column {
delete jobParams[setting.subkey];
}
}
// console.log(JSON.stringify(jobParams));
console.log(JSON.stringify(jobParams));
}
}

View file

@ -8,10 +8,9 @@ import "../components"
property var color: "white"
property int level: 100
property int high_level: 100
property int low_level: 10
property int low_level: 0
property string name: ""
property string type: ""
property real value_fraction: level/(high_level == 0 ? 100 : high_level)
spacing: Theme.paddingMedium
bottomPadding: Theme.paddingMedium
@ -20,14 +19,14 @@ import "../components"
{
anchors.verticalCenter: parent.verticalCenter
color: parent.color
value: value_fraction
value: (level/high_level)
}
Label
{
anchors.verticalCenter: parent.verticalCenter
width: Theme.itemSizeExtraSmall
text: ""+Math.round(100*value_fraction)+"%"
color: level <= low_level ? "red" : Theme.highlightColor
text: ""+(100*level/high_level)+"%"
color: Theme.highlightColor
font.pixelSize: Theme.fontSizeExtraSmall
}
Column

View file

@ -20,10 +20,12 @@ 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++) {

View file

@ -13,37 +13,9 @@ ApplicationWindow
{
id: appWin
property Page _mainPage
property bool expectCalligra: true
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
}
}
initialPage: Component { FirstPage { selectedFile: Qt.application.arguments[1] ? Qt.application.arguments[1] : "" } }
cover: Qt.resolvedUrl("cover/CoverPage.qml")
allowedOrientations: Orientation.All
@ -79,9 +51,11 @@ 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
}
@ -103,18 +77,10 @@ ApplicationWindow
}
function simplifyType(mimetype) {
if(mimetype == Mimer.SVG)
{
return {simple: "svg", translatable: qsTr("SVGs")};
}
else if(Mimer.isImage(mimetype))
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")};
@ -157,6 +123,7 @@ ApplicationWindow
expireTimeout: 4000
function notify(data) {
console.log("notifyMessage", data)
body = data
previewBody = data
publish()
@ -185,19 +152,5 @@ ApplicationWindow
defaultValue: SeaPrintSettings.ignoreSslErrorsDefault
}
ConfigurationValue
{
id: debugLogSetting
key: SeaPrintSettings.debugLogPath
defaultValue: SeaPrintSettings.debugLogDefault
}
ConfigurationValue
{
id: allowExternalConnectionsSetting
key: SeaPrintSettings.allowExternalConnectionsPath
defaultValue: SeaPrintSettings.allowExternalConnectionsDefault
}
}

View file

@ -62,7 +62,7 @@ Page {
AboutLabel {
font.pixelSize: Theme.fontSizeSmall
color: Theme.primaryColor
text: "© 2019-2023 Anton Thomasson et al."
text: "© 2019-2022 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, pherjung, phklrz, Quentí\n"+
qsTr("German")+" - black-sheep-dev, cnlpete, wheelseal\n"+
qsTr("Polish")+" - atlochowski, karolkurek\n"+
qsTr("Spanish")+" - carmenfdezb, JSEHV"
qsTr("French")+" - ensag-dev, Quentí\n"+
qsTr("German")+" - black-sheep-dev, wheelseal, cnlpete\n"+
qsTr("Polish")+" - atlochowski\n"+
qsTr("Spanish")+" - carmenfdezb"
}
SectionHeader { text: qsTr("Optional dependencies") }

View file

@ -3,8 +3,6 @@ import Sailfish.Silica 1.0
import "utils.js" as Utils
Page {
property bool busyPage: true
allowedOrientations: Orientation.All
Component.onCompleted: {

View file

@ -12,18 +12,6 @@ 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
@ -81,27 +69,6 @@ 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")
@ -148,24 +115,25 @@ Page {
}
SectionHeader {
text: qsTr("Performance")
visible: printer.attrs.hasOwnProperty("pages-per-minute") || printer.attrs.hasOwnProperty("pages-per-minute-color")
text: qsTr("Supply status")
visible: printer.attrs.hasOwnProperty("marker-colors")
}
Label
Repeater
{
text: ""+printer.attrs["pages-per-minute"].value+" "+qsTr("pages/min")
visible: printer.attrs.hasOwnProperty("pages-per-minute")
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]
}
}
Label
{
text: ""+printer.attrs["pages-per-minute-color"].value+" "+qsTr("pages/min (color)")
visible: printer.attrs.hasOwnProperty("pages-per-minute-color")
}
}
}
}

View file

@ -4,7 +4,6 @@ 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
@ -12,11 +11,16 @@ 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 {
@ -27,6 +31,7 @@ Page {
property bool initialSSIDchange: true
onSsidChanged: {
console.log("ssid changed", wifi.ssid);
if(!initialSSIDchange)
{
IppDiscovery.reset();
@ -57,6 +62,12 @@ Page {
Component.onCompleted: {
IppDiscovery.discover();
if(selectedFile != "")
{
var type = Mimer.get_type(selectedFile);
console.log(type);
selectedFileType = type;
}
}
property bool nagged: false
@ -64,6 +75,7 @@ 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
@ -95,6 +107,7 @@ 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);
})
@ -135,7 +148,7 @@ Page {
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(appWin.selectedFileType) != -1
property bool canPrint: supported_formats.mimetypes.indexOf(selectedFileType) != -1
Connections {
target: printer
@ -153,6 +166,7 @@ Page {
Connections {
target: page
onRefreshed: {
console.log("onRefreshed")
printer.refresh()
}
}
@ -174,7 +188,7 @@ Page {
{
if(printer.attrs.hasOwnProperty("printer-uuid"))
{
return JSON.parse(db.getJobSettings(printer.attrs["printer-uuid"].value, appWin.selectedFileType));
return JSON.parse(db.getJobSettings(printer.attrs["printer-uuid"].value, selectedFileType));
}
else
{
@ -191,7 +205,7 @@ Page {
return;
}
debugCountReset.restart();
if(appWin.selectedFile == "")
if(selectedFile == "")
{
noFileSelected();
}
@ -201,11 +215,11 @@ Page {
}
else
{
pageStack.push(Qt.resolvedUrl("PrinterPage.qml"), {printer: printer, selectedFile: appWin.selectedFile, jobParams: maybeGetParams()})
pageStack.push(Qt.resolvedUrl("PrinterPage.qml"), {printer: printer, selectedFile: selectedFile, jobParams: maybeGetParams()})
}
}
ImageItem {
Image {
id: icon
anchors.top: parent.top
anchors.left: parent.left
@ -214,17 +228,13 @@ Page {
height: Theme.itemSizeLarge
width: Theme.itemSizeLarge
image: printer.icon
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"
Image {
id: placeholder
anchors.fill: parent
sourceSize.height: height
sourceSize.width: width
visible: !parent.valid
source: "icon-seaprint-nobg.svg"
}
}
Image {
@ -242,13 +252,13 @@ Page {
Label {
id: name_label
color: canPrint || appWin.selectedFile == "" ? Theme.primaryColor : Theme.secondaryColor
color: canPrint || selectedFile == "" ? Theme.primaryColor : Theme.secondaryColor
text: name
}
Label {
id: mm_label
color: canPrint || appWin.selectedFile == "" ? Theme.primaryColor : Theme.secondaryColor
color: canPrint || 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 : "")
@ -256,7 +266,7 @@ Page {
Label {
id: uri_label
color: canPrint || appWin.selectedFile == "" ? Theme.highlightColor : Theme.secondaryColor
color: canPrint || selectedFile == "" ? Theme.highlightColor : Theme.secondaryColor
font.pixelSize: Theme.fontSizeTiny
text: printer.url
}
@ -277,7 +287,7 @@ Page {
width: Theme.itemSizeExtraSmall/2
visible: supported_formats.pdf
highlightColor: "red"
highlighted: !(appWin.selectedFile == "" || canPrint)
highlighted: !(selectedFile == "" || canPrint)
source: "image://theme/icon-m-file-pdf"
}
HighlightImage {
@ -285,7 +295,7 @@ Page {
width: Theme.itemSizeExtraSmall/2
visible: supported_formats.postscript
highlightColor: "red"
highlighted: !(appWin.selectedFile == "" || canPrint)
highlighted: !(selectedFile == "" || canPrint)
source: "image://theme/icon-m-file-other"
}
@ -294,7 +304,7 @@ Page {
width: Theme.itemSizeExtraSmall/2
visible: supported_formats.plaintext
highlightColor: "red"
highlighted: !(appWin.selectedFile == "" || canPrint)
highlighted: !(selectedFile == "" || canPrint)
source: "image://theme/icon-m-file-document"
}
HighlightImage {
@ -302,23 +312,15 @@ Page {
width: Theme.itemSizeExtraSmall/2
visible: supported_formats.office
highlightColor: "red"
highlighted: !(appWin.selectedFile == "" || canPrint)
highlighted: !(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: !(appWin.selectedFile == "" || canPrint)
highlighted: !(selectedFile == "" || canPrint)
source: "image://theme/icon-m-file-image"
}
}
@ -352,16 +354,12 @@ Page {
}
}
}
Rectangle {
DockedPanel {
id: fileDock
open: true
height: panelColumn.implicitHeight
width: parent.width
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) }
}
dock: Dock.Bottom
Column {
id: panelColumn
@ -373,7 +371,7 @@ Page {
Row {
id: warningRow
anchors.horizontalCenter: parent.horizontalCenter
visible: Mimer.isOffice(appWin.selectedFileType)
visible: Mimer.isOffice(page.selectedFileType)
HighlightImage {
source: "image://theme/icon-s-warning"
@ -396,7 +394,7 @@ Page {
horizontalAlignment: contentWidth > width ? Text.AlignRight : Text.AlignHCenter
truncationMode: TruncationMode.Fade
text: appWin.selectedFile != "" ? appWin.selectedFile : qsTr("No file selected")
text: selectedFile != "" ? selectedFile : qsTr("No file selected")
SequentialAnimation {
id: noFileSelectedAnimation
@ -409,6 +407,7 @@ Page {
Connections {
target: page
onNoFileSelected: {
console.log("onNoFileSelected")
noFileSelectedAnimation.start()
}
}
@ -449,7 +448,19 @@ Page {
title: qsTr("Choose file")
onSelectedContentPropertiesChanged: {
appWin.openFile(selectedContentProperties.filePath);
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 = ""
}
}
}
}
@ -459,7 +470,8 @@ Page {
allowedOrientations: Orientation.All
onSelectedContentPropertiesChanged: {
appWin.openFile(selectedContentProperties.filePath);
page.selectedFile = selectedContentProperties.filePath
page.selectedFileType = Mimer.get_type(selectedContentProperties.filePath)
}
}
}

View file

@ -10,6 +10,7 @@ Page {
Component.onCompleted: {
printer.getJobs()
console.log(JSON.stringify(printer.jobs))
}
Connections {
@ -36,6 +37,7 @@ Page {
MenuItem {
text: qsTr("Refresh")
onClicked: {
console.log(JSON.stringify(printer.jobs))
printer.getJobs();
}
}
@ -62,6 +64,7 @@ Page {
anchors.verticalCenter: parent.verticalCenter
color: Theme.highlightColor
text: printer.jobs[index]["job-id"].value
Component.onCompleted: console.log(JSON.stringify(printer.jobs))
}
Column {
@ -70,6 +73,7 @@ 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

View file

@ -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,16 +78,6 @@ 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

View file

@ -61,7 +61,7 @@ Page {
onClicked: {
var tmp = jobParams;
// Varies between documents, would be confusing to save
delete tmp["page-ranges"];
tmp["page-ranges"] = undefined;
db.setJobSettings(printer.attrs["printer-uuid"].value, selectedFileType, JSON.stringify(tmp))
}
}
@ -69,6 +69,7 @@ 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)
@ -104,8 +105,6 @@ Page {
tag: IppMsg.Integer
name: "copies"
prettyName: qsTr("Copies")
valid: _valid || Utils.supports_raster(printer)
minimum_high: 99
}
ChoiceSetting {
tag: IppMsg.Keyword
@ -120,7 +119,6 @@ Page {
property var pdfpages: ConvertChecker.pdfPages(selectedFile)
high: pdfpages == 0 ? 65535 : pdfpages
acceptRangeList: true
}
ChoiceSetting {
tag: IppMsg.Integer
@ -151,10 +149,10 @@ Page {
tag: IppMsg.Keyword
name: "print-scaling"
prettyName: qsTr("Scaling")
valid: _valid && Mimer.isImage(selectedFileType) && selectedFileType != Mimer.SVG
valid: _valid && selectedFileType == Mimer.JPEG
DependentOn {
target: transferFormatSetting
values: [Mimer.JPEG, Mimer.PNG]
values: [Mimer.JPEG]
}
}
ChoiceSetting {
@ -199,7 +197,6 @@ Page {
name: "media-top-margin"
subkey: "media-col"
prettyName: qsTr("Top")
valid: _valid && Mimer.isImage(selectedFileType)
}
ChoiceSetting {
visible: marginsButton.active
@ -207,7 +204,6 @@ Page {
name: "media-bottom-margin"
subkey: "media-col"
prettyName: qsTr("Bottom")
valid: _valid && Mimer.isImage(selectedFileType)
}
ChoiceSetting {
visible: marginsButton.active
@ -215,7 +211,6 @@ Page {
name: "media-left-margin"
subkey: "media-col"
prettyName: qsTr("Left")
valid: _valid && Mimer.isImage(selectedFileType)
}
ChoiceSetting {
visible: marginsButton.active
@ -223,7 +218,6 @@ Page {
name: "media-right-margin"
subkey: "media-col"
prettyName: qsTr("Right")
valid: _valid && Mimer.isImage(selectedFileType)
}
}
}

View file

@ -53,23 +53,6 @@ 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
}
}
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -4,14 +4,15 @@ Summary: SeaPrint
Version: 0
Release: 1
Group: Qt/Qt
License: GPLv3
URL: https://github.com/attah/harbour-seaprint
License: LICENSE
URL: http://example.org/
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)
@ -19,10 +20,6 @@ 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
@ -47,7 +44,7 @@ desktop-file-install --delete-original \
%files
%defattr(-,root,root,-)
%{_bindir}/%{name}
%{_bindir}
%{_datadir}/%{name}
%{_datadir}/applications/%{name}.desktop
%{_datadir}/icons/hicolor/*/apps/%{name}.png

View file

@ -4,6 +4,7 @@
#include "convertchecker.h"
#include <QProcess>
#include <QtDebug>
#include "mimer.h"
ConvertChecker::ConvertChecker() : poppler("libpoppler-glib.so.8")
{

584
src/convertworker.cpp Normal file
View file

@ -0,0 +1,584 @@
#include "convertworker.h"
#include <sailfishapp.h>
#include "papersizes.h"
#include "convertchecker.h"
#include "mimer.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(Bytestream msg)
{
CurlRequester cr(_printer->httpUrl());
cr.setFinishedCallback(_printer, &IppPrinter::getPrinterAttributesFinished);
qDebug() << "command...";
cr.write((char*)msg.raw(), msg.size());
}
// TODO: de-duplicate
void ConvertWorker::getJobs(Bytestream msg)
{
CurlRequester cr(_printer->httpUrl());
cr.setFinishedCallback(_printer, &IppPrinter::getJobsRequestFinished);
cr.write((char*)msg.raw(), msg.size());
}
void ConvertWorker::cancelJob(Bytestream msg)
{
CurlRequester cr(_printer->httpUrl());
cr.setFinishedCallback(_printer, &IppPrinter::cancelJobFinished);
cr.write((char*)msg.raw(), msg.size());
}
void ConvertWorker::justUpload(QString filename, Bytestream 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((char*)header.raw(), header.size()));
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, Bytestream 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((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);
});
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, Bytestream 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((char*)header.raw(), header.size()));
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, Bytestream 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, Bytestream 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;
}

71
src/convertworker.h Normal file
View file

@ -0,0 +1,71 @@
#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(Bytestream msg);
void getJobs(Bytestream msg);
void cancelJob(Bytestream msg);
void justUpload(QString filename, Bytestream header);
void convertPdf(QString filename, Bytestream 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, Bytestream header,
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
quint32 HwResX, quint32 HwResY, QMargins margins);
void convertOfficeDocument(QString filename, Bytestream 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, Bytestream 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

View file

@ -1,104 +1,28 @@
#include "curlrequester.h"
#include <algorithm>
#include "settings.h"
static size_t trampoline(char* dest, size_t size, size_t nmemb, void* userp)
CurlRequester::CurlRequester(QUrl addr) : _addr(addr), _canWrite(1), _canRead(), _reading(false), _done(false), _dest(nullptr), _size(0), _offset(0), _performer(addr, this)
{
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;
});
_performer.start();
}
CurlRequester::~CurlRequester()
{
await();
while(!_canWrite.tryAcquire(1, 500))
{
if(!_performer.isRunning())
{
break;
}
}
_done = true;
_canRead.release();
_performer.wait();
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)
@ -106,7 +30,7 @@ bool CurlRequester::write(const char *data, size_t size)
qDebug() << "write " << size;
while(!_canWrite.tryAcquire(1, 500))
{
if(!_worker.isRunning())
if(!_performer.isRunning())
{
return false;
}
@ -129,7 +53,7 @@ size_t CurlRequester::requestWrite(char* dest, size_t size)
if(!_reading)
{
_canRead.acquire();
if(_done) // Can only have been set by await() - only relevant to check if strating to write
if(_done) // Can only have been set by write() - only relevant to check if strating to write
{
return 0;
}

View file

@ -4,55 +4,30 @@
#include <QUrl>
#include <QThread>
#include <QSemaphore>
#include <QtConcurrent/QtConcurrent>
#include <QMetaMethod>
#include <curl/curl.h>
#include <bytestream.h>
#include <QDebug>
#include <functional>
#include "curlworker.h"
class CurlRequester : public QObject
{
Q_OBJECT
public:
enum Role {
IppRequest,
HttpGetRequest
};
CurlRequester(QUrl addr, Role role = IppRequest, Bytestream* = nullptr);
CurlRequester(QUrl addr);
~CurlRequester();
CURLcode await(Bytestream* = nullptr);
template<typename Class, typename Callback>
bool setFinishedCallback(const Class* receiverObject, Callback cb)
{
connect(&_performer, &CurlWorker::done, receiverObject, cb);
return true;
}
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;
@ -60,19 +35,11 @@ private:
bool _reading;
bool _done;
CURLcode _result;
Bytestream _resultMsg;
char* _dest;
size_t _size;
size_t _offset;
friend class CurlWorker;
CURL* _curl;
struct curl_slist* _opts = NULL;
QFuture<void> _worker;
CurlWorker _performer;
};
#endif // CURLREQUESTER_H

57
src/curlworker.cpp Normal file
View file

@ -0,0 +1,57 @@
#include "curlworker.h"
#include "curlrequester.h"
#include "settings.h"
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)
{
_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, "Expect:");
_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(){
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);
emit done(res, buf);
}

51
src/curlworker.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef CURLWORKER_H
#define CURLWORKER_H
#include <QThread>
#include <QUrl>
#include <curl/curl.h>
#include <QtDebug>
#include <bytestream.h>
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;
((Bytestream*)userdata)->putBytes(ptr, bytes_to_write);
return bytes_to_write;
}
signals:
void done(CURLcode, Bytestream);
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

View file

@ -1,25 +0,0 @@
#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();
}

View file

@ -1,24 +0,0 @@
#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

View file

@ -1,31 +0,0 @@
#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");
}

View file

@ -1,21 +0,0 @@
#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

View file

@ -1,23 +1,15 @@
#include <QtQuick>
#include <QVariant>
#include <QDBusConnection>
#include <sailfishapp.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)
@ -32,23 +24,8 @@ 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);
@ -59,36 +36,17 @@ 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<Settings>("seaprint.settings", 1, 0, "SeaPrintSettings", singletontype_provider<Settings>);
qmlRegisterSingletonType<RangeListChecker>("seaprint.rangelistchecker", 1, 0, "RangeListChecker", singletontype_provider<RangeListChecker>);
qmlRegisterSingletonType<ConvertChecker>("seaprint.settings", 1, 0, "SeaPrintSettings", singletontype_provider<Settings>);
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();
}
view->show();
return app->exec();
}

View file

@ -1,29 +0,0 @@
#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();
}

View file

@ -1,31 +0,0 @@
#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

View file

@ -37,7 +37,7 @@ QStringList get_addr(Bytestream& bts)
return addr;
}
IppDiscovery::IppDiscovery() : QStringListModel()
IppDiscovery::IppDiscovery() : QStringListModel(), QQuickImageProvider(QQuickImageProvider::Image, ForceAsynchronousImageLoading)
{
socket = new QUdpSocket(this);
connect(socket, &QUdpSocket::readyRead, this, &IppDiscovery::readPendingDatagrams);
@ -93,15 +93,15 @@ void IppDiscovery::sendQuery(quint16 qtype, QStringList addrs) {
QTime now = QTime::currentTime();
QTime aWhileAgo = now.addSecs(-1);
for(QPair<quint16, QString> oq : _outstandingQueries.keys())
foreach(QString oq, _outstandingQueries.keys())
{
if(_outstandingQueries[oq] < aWhileAgo)
{ // Housekeeping for _outstandingQueries
_outstandingQueries.remove(oq);
}
else if(oq.first == qtype && addrs.contains(oq.second))
else if(addrs.contains(oq))
{ // we recently asked about this, remove it
addrs.removeOne(oq.second);
addrs.removeOne(oq);
}
}
@ -121,9 +121,9 @@ void IppDiscovery::sendQuery(quint16 qtype, QStringList addrs) {
query << _transactionid++ << flags << questions << (quint16)0 << (quint16)0 << (quint16)0;
for(QString addr : addrs)
foreach(QString addr, addrs)
{
_outstandingQueries.insert({qtype, addr}, now);
_outstandingQueries.insert(addr, now);
QStringList addrParts = addr.split(".");
QString addrPart, restAddr;
@ -153,7 +153,9 @@ void IppDiscovery::sendQuery(quint16 qtype, QStringList addrs) {
}
socket->writeDatagram((char*)(query.raw()), query.size(), QHostAddress("224.0.0.251"), 5353);
QByteArray bytes((char*)(query.raw()), query.size());
socket->writeDatagram(bytes, QHostAddress("224.0.0.251"), 5353);
}
QString make_addr(QString proto, int defaultPort, quint16 port, QString ip, QString rp)
@ -185,15 +187,11 @@ void IppDiscovery::update()
QList<QPair<QString,QString>> ippsIpRps;
QString target, rp;
for(QString it : _ipps)
foreach(QString it, _ipps)
{
if(!_targets.contains(it) || !_ports.contains(it) || !_rps.contains(it))
continue;
quint16 port = _ports.value(it);
target = _targets.value(it);
rp = _rps.value(it);
quint16 port = _ports[it];
target = _targets[it];
rp = _rps[it];
for(QMultiMap<QString,QString>::Iterator ait = _AAs.begin(); ait != _AAs.end(); ait++)
{
@ -210,14 +208,11 @@ void IppDiscovery::update()
}
}
for(QString it : _ipp)
foreach(QString it, _ipp)
{
if(!_targets.contains(it) || !_ports.contains(it) || !_rps.contains(it))
continue;
quint16 port = _ports.value(it);
target = _targets.value(it);
rp = _rps.value(it);
quint16 port = _ports[it];
target = _targets[it];
rp = _rps[it];
for(QMultiMap<QString,QString>::Iterator ait = _AAs.begin(); ait != _AAs.end(); ait++)
{
@ -239,9 +234,9 @@ void IppDiscovery::update()
// Counting on that _ipp duplicates doesn't resolve fully any erlier than their _ipps counterpart
// TODO?: replace this with some logic that can bpoth add and remove
// TODO?: replace this with some logica that can bpoth add and remove
// and it can consider _favourites, so we can drop cleanUpdate
for(QString f : found)
foreach(QString f, found)
{
if(!this->stringList().contains(f))
{
@ -257,23 +252,16 @@ void IppDiscovery::update()
void IppDiscovery::updateAndQueryPtrs(QStringList& ptrs, QStringList new_ptrs)
{
new_ptrs.removeDuplicates();
for(QString ptr : new_ptrs)
foreach(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(!_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});
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});
}
}
}
@ -340,7 +328,6 @@ void IppDiscovery::readPendingDatagrams()
{
std::string tmprp;
tmp/tmp.remaining() >> tmprp;
qDebug() << "tmprp" << aaddr << tmprp.c_str();
_rps[aaddr] = tmprp.c_str();
}
}
@ -383,7 +370,7 @@ void IppDiscovery::readPendingDatagrams()
qDebug() << "new ipp ptrs" << new_ipp_ptrs;
qDebug() << "new ipps ptrs" << new_ipps_ptrs;
qDebug() << "ipp ptrs" << _ipp;
qDebug() << "ipps ptrs" << _ipps;
qDebug() << "ipp ptrs" << _ipps;
qDebug() << "rps" << _rps;
qDebug() << "ports" << _ports;
qDebug() << "new targets" << new_targets;
@ -393,12 +380,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(_ipps, new_ipps_ptrs);
updateAndQueryPtrs(_ipp, new_ipp_ptrs);
updateAndQueryPtrs(_ipps, new_ipps_ptrs);
QStringList unresolvedAddrs;
for(QString t : new_targets)
foreach(QString t, new_targets)
{
// If target does not resolve to an address, query about it
if(!_AAs.contains(t))
@ -417,8 +404,15 @@ void IppDiscovery::readPendingDatagrams()
}
bool IppDiscovery::resolve(QUrl& url)
{
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);
QString host = url.host();
if(host.endsWith("."))
@ -426,11 +420,31 @@ bool IppDiscovery::resolve(QUrl& url)
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;
}
return false;
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;
}

View file

@ -1,11 +1,16 @@
#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
class IppDiscovery : public QStringListModel, public QQuickImageProvider
{
Q_OBJECT
public:
@ -16,8 +21,6 @@ public:
Q_INVOKABLE void discover();
Q_INVOKABLE void reset();
bool resolve(QUrl& url);
signals:
void favouritesChanged();
void ignoreSslErrorsChanged();
@ -44,6 +47,9 @@ 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;
@ -53,7 +59,7 @@ private:
QMultiMap<QString,QString> _AAs;
QMultiMap<QString,QString> _AAAAs;
QMap<QPair<quint16, QString>, QTime> _outstandingQueries;
QMap<QString, QTime> _outstandingQueries;
QStringList _favourites;
QUdpSocket* socket;

View file

@ -6,9 +6,8 @@ IppMsg::IppMsg()
{
}
IppMsg::IppMsg(Operation operation, QJsonObject opAttrs, QJsonObject jobAttrs, quint8 majVsn, quint8 minVsn)
IppMsg::IppMsg(QJsonObject opAttrs, QJsonObject jobAttrs, quint8 majVsn, quint8 minVsn)
{
_operation = operation;
_majVsn = majVsn;
_minVsn = minVsn;
_opAttrs = opAttrs;
@ -77,63 +76,40 @@ QJsonValue IppMsg::consume_value(quint8 tag, Bytestream& data)
Q_ASSERT(false);
case Integer:
case Enum:
{
quint32 tmp_u32=0;
if(!(data >>= (quint16)0))
{
data >> (quint16)4 >> tmp_u32;
}
quint32 tmp_u32;
data >> tmp_len >> tmp_u32;
value = (int)tmp_u32;
break;
}
case Boolean:
{
quint8 tmp_bool=0;
if(!(data >>= (quint16)0))
{
data >> (quint16)1 >> tmp_bool;
}
quint8 tmp_bool;
data >> tmp_len >> 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 >> (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);
tmp_datetime = QDateTime(date, time, Qt::OffsetFromUTC, offset_seconds);
}
value = tmp_datetime.toString(Qt::ISODate);
quint16 year;
quint8 month, day, hour, minutes, seconds, deci_seconds,
plus_minus, utc_h_offset, utc_m_offset;
data >> tmp_len >> year >> month >> day >> hour >> minutes >> seconds >> deci_seconds
>> plus_minus >> utc_h_offset >> utc_m_offset;
QDate date(year, month, day);
QTime time(hour, minutes, seconds, deci_seconds*100);
int offset_seconds = (plus_minus == '+' ? 1 : -1)*(utc_h_offset*60*60+utc_m_offset*60);
value = QDateTime(date, time, Qt::OffsetFromUTC, offset_seconds).toString(Qt::ISODate);
break;
}
case Resolution:
{
qint32 x=0;
qint32 y=0;
qint8 units=0;
if(!(data >>= (quint16)0))
{
data >> (quint16)9 >> x >> y >> units;
}
qint32 x, y;
qint8 units;
data >> tmp_len >> x >> y >> units;
value = QJsonObject {{"x", x}, {"y", y}, {"units", units}};
break;
}
case IntegerRange:
{
qint32 low=0;
qint32 high=0;
if(!(data >>= (quint16)0))
{
data >> (quint16)8 >> low >> high;
}
qint32 low, high;
data >> tmp_len >> low >> high;
value = QJsonObject {{"low", low}, {"high", high}};
break;
}
@ -183,7 +159,7 @@ QJsonArray IppMsg::get_unnamed_attributes(Bytestream& data)
QJsonArray untag_values(QJsonArray taggedValues)
{
QJsonArray res;
for(QJsonValue it : taggedValues)
foreach(QJsonValue it, taggedValues)
{
res.append(it.toObject()["value"]);
}
@ -275,11 +251,17 @@ 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}};
}
@ -288,7 +270,6 @@ 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)
@ -304,13 +285,13 @@ QString IppMsg::consume_attribute(QJsonObject& attrs, Bytestream& data)
return name;
}
Bytestream IppMsg::encode()
Bytestream IppMsg::encode(Operation op)
{
Bytestream ipp;
ipp << _majVsn << _minVsn;
ipp << quint16(_operation);
ipp << quint16(op);
ipp << _reqid++;
@ -320,15 +301,11 @@ Bytestream IppMsg::encode()
QStringList InitialAttrs = {"attributes-charset",
"attributes-natural-language",
"printer-uri",
"job-id",
"requesting-user-name"};
for(QString key : InitialAttrs)
foreach(QString key, InitialAttrs)
{
if(_opAttrs.find(key) != _opAttrs.end())
{
QJsonObject val = _opAttrs.take(key).toObject();
encode_attr(ipp, val["tag"].toInt(), key, val["value"]);
}
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
@ -362,25 +339,7 @@ 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:
@ -397,7 +356,7 @@ void IppMsg::encode_value(Bytestream& msg, quint8 tag, QJsonValue value)
}
case Boolean:
{
quint8 tmp_u8 = value.toBool();
quint32 tmp_u8 = value.toBool();
msg << (quint16)1 << tmp_u8;
break;
}
@ -424,11 +383,19 @@ void IppMsg::encode_value(Bytestream& msg, quint8 tag, QJsonValue value)
case BeginCollection:
{
msg << (quint16)0; // length of value
QJsonObject collection = value.toObject();
for(QString key : collection.keys())
if(value.isObject())
{
encode_attr(msg, collection[key].toObject()["tag"].toInt(), key,
collection[key].toObject()["value"], true);
QJsonObject collection = value.toObject();
foreach(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;
@ -456,8 +423,3 @@ void IppMsg::encode_value(Bytestream& msg, quint8 tag, QJsonValue value)
break;
}
}
void IppMsg::setOpAttr(QString name, IppMsg::IppTag type, QJsonValue value)
{
_opAttrs.insert(name, QJsonObject {{"tag", type}, {"value", value}});
}

View file

@ -4,11 +4,11 @@
#include "bytestream.h"
#include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QDate>
#include <QTime>
#include <QDebug>
#include <QUrl>
#include <QtNetwork>
#include <QNetworkReply>
#include <QIODevice>
class IppMsg
{
@ -61,23 +61,20 @@ public:
RestartJob = 0x000E,
PausePrinter = 0x0010,
ResumePrinter = 0x0011,
PurgeJobs = 0x0012,
IdentifyPrinter = 0x003C
PurgeJobs = 0x0012
};
explicit IppMsg();
explicit IppMsg(Bytestream& resp);
IppMsg(Operation operation, QJsonObject opAttrs, QJsonObject jobAttrs=QJsonObject(), quint8 majVsn=1, quint8 minVsn=1);
IppMsg(QJsonObject opAttrs, QJsonObject jobAttrs=QJsonObject(), quint8 majVsn=1, quint8 minVsn=1);
IppMsg(const IppMsg& other) = default;
~IppMsg();
Bytestream encode();
Bytestream encode(Operation op);
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;}
@ -88,9 +85,6 @@ 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;

View file

@ -1,41 +1,42 @@
#include "ippprinter.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*");
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);
_worker = new ConvertWorker(this);
_worker->moveToThread(&_workerThread);
connect(this, &IppPrinter::doGetStrings, _worker, &PrinterWorker::getStrings);
connect(this, &IppPrinter::doGetImage, _worker, &PrinterWorker::getImage);
connect(&_workerThread, &QThread::finished, _worker, &QObject::deleteLater);
connect(_worker, &PrinterWorker::progress, this, &IppPrinter::setProgress);
connect(_worker, &PrinterWorker::busyMessage, this, &IppPrinter::setBusyMessage);
connect(_worker, &PrinterWorker::failed, this, &IppPrinter::convertFailed);
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);
qRegisterMetaType<QMargins>();
qRegisterMetaType<IppMsg>();
_workerThread.start();
}
IppPrinter::~IppPrinter() {
// TODO: delete worker and workerthread?
}
QJsonObject IppPrinter::opAttrs() {
@ -54,6 +55,8 @@ 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")
{
@ -83,110 +86,44 @@ 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();
if(localFile.endsWith(".raw") || localFile.endsWith(".bin"))
QFile file(_url.toLocalFile());
if(file.open(QIODevice::ReadOnly))
{
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();
}
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);
}
else
{
QFile file(localFile);
if(file.open(QIODevice::ReadOnly))
{
QJsonDocument JsonDocument = QJsonDocument::fromJson(file.readAll());
_attrs = JsonDocument.object();
file.close();
}
}
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(IppMsg::GetPrinterAttrs, o);
emit doDoGetPrinterAttributes(msg.encode());
IppMsg msg = IppMsg(o);
emit doCommand(msg.encode(IppMsg::GetPrinterAttrs));
}
}
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();
@ -238,8 +175,6 @@ void IppPrinter::getPrinterAttributesFinished(CURLcode res, Bytestream data)
emit attrsChanged();
MaybeGetStrings();
MaybeGetIcon();
UpdateAdditionalDocumentFormats();
}
@ -312,48 +247,14 @@ void IppPrinter::cancelJobFinished(CURLcode res, Bytestream data)
getJobs();
}
void IppPrinter::identifyFinished(CURLcode /*res*/, Bytestream /*data*/)
{
}
void IppPrinter::getStringsFinished(CURLcode res, Bytestream data)
void IppPrinter::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
{
qDebug() << res << data.size();
if(res == CURLE_OK)
bool ignore = Settings::instance()->ignoreSslErrors();
qDebug() << reply->request().url() << "SSL handshake failed" << errors << ignore;
if(ignore)
{
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);
}
}
reply->ignoreSslErrors(errors);
}
}
@ -381,27 +282,17 @@ QString targetFormatIfAuto(QString documentFormat, QString mimeType, QJsonArray
{
if(documentFormat == Mimer::OctetStream)
{
QStringList PdfPrioList = {Mimer::PDF, Mimer::Postscript, Mimer::PWG, Mimer::URF};
if(mimeType == Mimer::PDF)
if(mimeType == Mimer::PDF || mimeType == Mimer::Plaintext)
{
return firstMatch(supportedMimeTypes, PdfPrioList);
return firstMatch(supportedMimeTypes, {Mimer::PDF, Mimer::Postscript, Mimer::PWG, Mimer::URF });
}
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, PdfPrioList);
}
else if(documentFormat == Mimer::SVG)
{
QStringList SvgPrioList {Mimer::PWG, Mimer::URF, Mimer::PDF, Mimer::Postscript};
return firstMatch(supportedMimeTypes, SvgPrioList);
return firstMatch(supportedMimeTypes, {Mimer::PDF, Mimer::Postscript, Mimer::PWG, Mimer::URF });
}
else if(Mimer::isImage(mimeType))
{
@ -419,28 +310,29 @@ QString targetFormatIfAuto(QString documentFormat, QString mimeType, QJsonArray
return documentFormat;
}
void IppPrinter::adjustRasterSettings(QString filename, QString mimeType, QJsonObject& jobAttrs, PrintParameters& Params)
void IppPrinter::adjustRasterSettings(QString documentFormat, QJsonObject& jobAttrs, quint32& HwResX, quint32& HwResY,
bool& BackHFlip, bool& BackVFlip)
{
if(Params.format != PrintParameters::PWG && Params.format != PrintParameters::URF)
if(documentFormat != Mimer::PWG && documentFormat != Mimer::URF)
{
return;
}
jobAttrs.remove("printer-resolution");
//TODO? jobAttrs.remove("printer-resolution");
if(Params.format == PrintParameters::PWG)
if(documentFormat == Mimer::PWG)
{
quint32 diff = std::numeric_limits<quint32>::max();
quint32 AdjustedHwResX = Params.hwResW;
quint32 AdjustedHwResY = Params.hwResH;
for(QJsonValue res : _attrs["pwg-raster-document-resolution-supported"].toObject()["value"].toArray())
quint32 AdjustedHwResX = HwResX;
quint32 AdjustedHwResY = HwResY;
foreach(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(Params.hwResW-resObj["x"].toInt())) + std::abs(int(Params.hwResH-resObj["y"].toInt()));
quint32 tmpDiff = std::abs(int(HwResX-resObj["x"].toInt())) + std::abs(int(HwResY-resObj["y"].toInt()));
if(tmpDiff < diff)
{
diff = tmpDiff;
@ -448,26 +340,27 @@ void IppPrinter::adjustRasterSettings(QString filename, QString mimeType, QJsonO
AdjustedHwResY = resObj["y"].toInt();
}
}
Params.hwResW = AdjustedHwResX;
Params.hwResH = AdjustedHwResY;
HwResX = AdjustedHwResX;
HwResY = AdjustedHwResY;
}
else if(Params.format == PrintParameters::URF)
{ // Ensure Params.format resolution for URF
Params.hwResW = Params.hwResH = std::min(Params.hwResW, Params.hwResH);
if(documentFormat == Mimer::URF)
{ // Ensure symmetric resolution for URF
HwResX = HwResY = std::min(HwResX, HwResY);
quint32 diff = std::numeric_limits<quint32>::max();
quint32 AdjustedHwRes = Params.hwResW;
quint32 AdjustedHwRes = HwResX;
QJsonArray urfSupported = _attrs["urf-supported"].toObject()["value"].toArray();
for(QJsonValue us : urfSupported)
foreach(QJsonValue us, urfSupported)
{
if(us.toString().startsWith("RS"))
{ //RS300[-600]
QStringList resolutions = us.toString().mid(2).split("-");
for(QString res : resolutions)
foreach(QString res, resolutions)
{
int intRes = res.toInt();
quint32 tmpDiff = std::abs(int(Params.hwResW - intRes));
quint32 tmpDiff = std::abs(int(HwResX - intRes));
if(tmpDiff < diff)
{
diff = tmpDiff;
@ -475,7 +368,7 @@ void IppPrinter::adjustRasterSettings(QString filename, QString mimeType, QJsonO
}
}
Params.hwResW = Params.hwResH = AdjustedHwRes;
HwResX = HwResY = AdjustedHwRes;
break;
}
}
@ -485,105 +378,64 @@ void IppPrinter::adjustRasterSettings(QString filename, QString mimeType, QJsonO
if(Sides != "" && Sides != "one-sided")
{
if(Params.format == PrintParameters::PWG)
if(documentFormat == Mimer::PWG)
{
QString DocumentSheetBack = _attrs["pwg-raster-document-sheet-back"].toObject()["value"].toString();
if(Sides=="two-sided-long-edge")
{
if(DocumentSheetBack=="flipped")
{
Params.backVFlip=true;
BackVFlip=true;
}
else if(DocumentSheetBack=="rotated")
{
Params.backHFlip=true;
Params.backVFlip=true;
BackHFlip=true;
BackVFlip=true;
}
}
else if(Sides=="two-sided-short-edge")
{
if(DocumentSheetBack=="flipped")
{
Params.backHFlip=true;
BackHFlip=true;
}
else if(DocumentSheetBack=="manual-tumble")
{
Params.backHFlip=true;
Params.backVFlip=true;
BackHFlip=true;
BackVFlip=true;
}
}
}
else if(Params.format == PrintParameters::URF)
else if(documentFormat == Mimer::URF)
{
QJsonArray URfSupported = _attrs["urf-supported"].toObject()["value"].toArray();
if(Sides=="two-sided-long-edge")
{
if(URfSupported.contains("DM2"))
{
Params.backVFlip=true;
BackVFlip=true;
}
else if(URfSupported.contains("DM3"))
{
Params.backHFlip=true;
Params.backVFlip=true;
BackHFlip=true;
BackVFlip=true;
}
}
else if(Sides=="two-sided-short-edge")
{
if(URfSupported.contains("DM2"))
{
Params.backHFlip=true;
BackHFlip=true;
}
else if(URfSupported.contains("DM4"))
{
Params.backHFlip=true;
Params.backVFlip=true;
BackHFlip=true;
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)
@ -593,7 +445,6 @@ void IppPrinter::print(QJsonObject jobAttrs, QString filename)
_progress = "";
emit progressChanged();
PrintParameters Params;
QFileInfo fileinfo(filename);
if(!fileinfo.exists())
@ -614,48 +465,22 @@ void IppPrinter::print(QJsonObject jobAttrs, QString filename)
qDebug() << supportedMimeTypes << supportedMimeTypes.contains(mimeType);
if(!Params.setPaperSize(getAttrOrDefault(jobAttrs, "media").toString(Params.paperSizeName.c_str()).toStdString()))
{
emit convertFailed(tr("Unsupported paper size"));
return;
}
QJsonObject o = opAttrs();
o.insert("job-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", fileinfo.fileName()}});
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");
}
}
QString PaperSize = getAttrOrDefault(jobAttrs, "media").toString();
if(jobAttrs.contains("media-col") && jobAttrs.contains("media"))
{
qDebug() << "moving media to media-col" << Params.paperSizeName.c_str();
qDebug() << "moving media to media-col" << PaperSize;
if(!PaperSizes.contains(PaperSize))
{
emit convertFailed(tr("Unknown document format dimensions"));
return;
}
int x = Params.getPaperSizeWInMillimeters()*100;
int y = Params.getPaperSizeHInMillimeters()*100;
int x = PaperSizes[PaperSize].width()*100;
int y = PaperSizes[PaperSize].height()*100;
QJsonObject Dimensions =
{{"tag", IppMsg::BeginCollection},
@ -674,114 +499,125 @@ void IppPrinter::print(QJsonObject jobAttrs, QString filename)
jobAttrs.remove("media");
}
QJsonObject jobOpAttrs = opAttrs();
QString documentFormat = getAttrOrDefault(jobAttrs, "document-format").toString();
qDebug() << "target format:" << documentFormat;
// document-format goes in the op-attrs and not the job-attrs
jobOpAttrs.insert("document-format", QJsonObject {{"tag", IppMsg::MimeMediaType}, {"value", targetFormat}});
o.insert("document-format", QJsonObject {{"tag", IppMsg::MimeMediaType}, {"value", documentFormat}});
jobAttrs.remove("document-format");
targetFormat = targetFormatIfAuto(targetFormat, mimeType, supportedMimeTypes);
qDebug() << "adjusted target format:" << targetFormat;
documentFormat = targetFormatIfAuto(documentFormat, mimeType, supportedMimeTypes);
qDebug() << "adjusted target format:" << documentFormat;
if(targetFormat == "" || targetFormat == Mimer::OctetStream)
if(documentFormat == "" || documentFormat == Mimer::OctetStream)
{
emit convertFailed(tr("Unknown document format"));
return;
}
if(targetFormat == Mimer::PDF)
{
Params.format = PrintParameters::PDF;
}
else if(targetFormat == Mimer::Postscript)
{
Params.format = PrintParameters::Postscript;
}
else if(targetFormat == Mimer::PWG)
{
Params.format = PrintParameters::PWG;
}
else if(targetFormat == Mimer::URF)
{
Params.format = PrintParameters::URF;
}
else
{
Params.format = PrintParameters::Invalid;
}
qDebug() << "Printing job" << jobOpAttrs << jobAttrs;
qDebug() << "Printing job" << o << jobAttrs;
QJsonValue PrinterResolutionRef = getAttrOrDefault(jobAttrs, "printer-resolution");
Params.hwResW = PrinterResolutionRef.toObject()["x"].toInt(Params.hwResW);
Params.hwResH = PrinterResolutionRef.toObject()["y"].toInt(Params.hwResH);
quint32 HwResX = PrinterResolutionRef.toObject()["x"].toInt(300);
quint32 HwResY = PrinterResolutionRef.toObject()["y"].toInt(300);
bool BackHFlip = false;
bool BackVFlip = false;
// 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(documentFormat, jobAttrs, HwResX, HwResY, BackHFlip, BackVFlip);
adjustRasterSettings(filename, mimeType, jobAttrs, Params);
QString Sides = getAttrOrDefault(jobAttrs, "sides").toString();
if(Sides=="two-sided-long-edge")
{
Params.duplex = true;
}
else if(Sides=="two-sided-short-edge")
{
Params.duplex = true;
Params.tumble = true;
}
Params.quality = getAttrOrDefault(jobAttrs, "print-quality").toInt();
quint32 Quality = getAttrOrDefault(jobAttrs, "print-quality").toInt();
QString PrintColorMode = getAttrOrDefault(jobAttrs, "print-color-mode").toString();
Params.colors = PrintColorMode.contains("color") ? 3 : PrintColorMode.contains("monochrome") ? 1 : Params.colors;
quint32 Colors = PrintColorMode.contains("color") ? 3 : PrintColorMode.contains("monochrome") ? 1 : 0;
QJsonArray supportedOperations = _attrs["operations-supported"].toObject()["value"].toArray();
if(supportedOperations.contains(IppMsg::CreateJob) && supportedOperations.contains(IppMsg::SendDocument))
quint32 PageRangeLow = 0;
quint32 PageRangeHigh = 0;
if(jobAttrs.contains("page-ranges"))
{
QJsonObject createJobOpAttrs = opAttrs();
createJobOpAttrs.insert("job-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", fileinfo.fileName()}});
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");
}
}
qDebug() << "Final create op attributes:" << createJobOpAttrs;
qDebug() << "Final job attributes:" << jobAttrs;
qDebug() << "Final op attributes:" << o;
qDebug() << "Final job attributes:" << jobAttrs;
IppMsg createJob = mk_msg(IppMsg::CreateJob, createJobOpAttrs, jobAttrs);
qDebug() << "Final job op attributes:" << jobOpAttrs;
IppMsg job = mk_msg(o, jobAttrs);
Bytestream contents = job.encode(IppMsg::PrintJob);
IppMsg sendDoc = mk_msg(IppMsg::SendDocument, jobOpAttrs);
emit doPrint2(filename, mimeType, targetFormat, createJob, sendDoc, Params, margins);
// Shouldn't and can't process these formats respectively
if((mimeType == documentFormat) && (documentFormat == Mimer::JPEG || documentFormat == Mimer::Postscript))
{
emit doJustUpload(filename, contents);
}
else
{
jobOpAttrs.insert("job-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", fileinfo.fileName()}});
if(PaperSize == "")
{
PaperSize = "iso_a4_210x297mm";
}
else if(!PaperSizes.contains(PaperSize))
{
emit convertFailed(tr("Unsupported print media"));
return;
}
qDebug() << "Final op attributes:" << jobOpAttrs;
qDebug() << "Final job attributes:" << jobAttrs;
bool TwoSided = false;
bool Tumble = false;
QString Sides = getAttrOrDefault(jobAttrs, "sides").toString();
IppMsg job = mk_msg(IppMsg::PrintJob, jobOpAttrs, jobAttrs);
emit doPrint(filename, mimeType, targetFormat, job, Params, margins);
if(Sides=="two-sided-long-edge")
{
TwoSided = true;
}
else if(Sides=="two-sided-short-edge")
{
TwoSided = true;
Tumble = true;
}
setBusyMessage(tr("Preparing"));
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());
emit doConvertImage(filename, contents, documentFormat, Colors, Quality,
PaperSize, HwResX, HwResY, margins);
}
else if(Mimer::isOffice(mimeType))
{
emit doConvertOfficeDocument(filename, contents, documentFormat, Colors, Quality,
PaperSize, HwResX, HwResY, TwoSided, Tumble, PageRangeLow, PageRangeHigh,
BackHFlip, BackVFlip);
}
else
{
emit convertFailed(tr("Cannot convert this file format"));
return;
}
}
return;
}
bool IppPrinter::getJobs() {
@ -790,9 +626,9 @@ bool IppPrinter::getJobs() {
QJsonObject o = opAttrs();
o.insert("requested-attributes", QJsonObject {{"tag", IppMsg::Keyword}, {"value", "all"}});
IppMsg job = IppMsg(IppMsg::GetJobs, o, QJsonObject());
IppMsg job = IppMsg(o, QJsonObject());
emit doGetJobs(job.encode());
emit doGetJobs(job.encode(IppMsg::GetJobs));
return true;
}
@ -804,29 +640,16 @@ bool IppPrinter::cancelJob(qint32 jobId) {
QJsonObject o = opAttrs();
o.insert("job-id", QJsonObject {{"tag", IppMsg::Integer}, {"value", jobId}});
IppMsg job = IppMsg(IppMsg::CancelJob, o, QJsonObject());
IppMsg job = IppMsg(o, QJsonObject());
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());
emit doCancelJob(job.encode(IppMsg::CancelJob));
return true;
}
bool IppPrinter::correctSuffix()
{
for(QJsonValue u : _attrs["printer-uri-supported"].toObject()["value"].toArray())
foreach(QJsonValue u, _attrs["printer-uri-supported"].toObject()["value"].toArray())
{
QUrl url(u.toString());
if(url.path() == _url.path())
@ -840,7 +663,7 @@ bool IppPrinter::correctSuffix()
QStringList IppPrinter::suffixes()
{
QStringList res;
for(QJsonValue u : _attrs["printer-uri-supported"].toObject()["value"].toArray())
foreach(QJsonValue u, _attrs["printer-uri-supported"].toObject()["value"].toArray())
{
QUrl url(u.toString());
if(!res.contains(url.path()))
@ -888,13 +711,6 @@ 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 == "")
@ -922,34 +738,13 @@ QJsonValue IppPrinter::getAttrOrDefault(QJsonObject jobAttrs, QString name, QStr
}
}
IppMsg IppPrinter::mk_msg(IppMsg::Operation operation, QJsonObject opAttrs, QJsonObject jobAttrs)
IppMsg IppPrinter::mk_msg(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(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, 2, 0);
}
return IppMsg(opAttrs, jobAttrs);
}

View file

@ -1,24 +1,23 @@
#ifndef IPPPRINTER_H
#define IPPPRINTER_H
#include <QtNetwork>
#include <QNetworkAccessManager>
#include "ippmsg.h"
#include "printerworker.h"
#include "convertworker.h"
#include "curlrequester.h"
#include <QImage>
#include <mlite5/MGConfItem>
class IppPrinter : public QObject
{
Q_OBJECT
friend class PrinterWorker;
friend class ConvertWorker;
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)
@ -38,7 +37,6 @@ public:
Q_INVOKABLE bool getJobs();
Q_INVOKABLE bool cancelJob(qint32 jobId);
Q_INVOKABLE bool identify();
bool correctSuffix();
QStringList suffixes();
@ -49,34 +47,32 @@ signals:
void jobAttrsChanged();
void jobsChanged();
void stringsChanged();
void iconChanged();
void jobFinished(bool status);
void cancelStatus(bool status);
void doDoGetPrinterAttributes(Bytestream msg);
void doCommand(Bytestream msg);
void doGetJobs(Bytestream msg);
void doCancelJob(Bytestream msg);
void doIdentify(Bytestream msg);
void doJustUpload(QString filename, Bytestream header);
void doFixupPlaintext(QString filename, Bytestream header);
void doPrintImageAsImage(QString filename, Bytestream header, QString targetFormat);
void doConvertPdf(QString filename, Bytestream header, PrintParameters Params);
void doConvertPdf(QString filename, Bytestream 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 doConvertImage(QString filename, Bytestream header, PrintParameters Params, QMargins margins);
void doConvertImage(QString filename, Bytestream header,
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
quint32 HwResX, quint32 HwResY, QMargins margins);
void doConvertOfficeDocument(QString filename, Bytestream header, PrintParameters Params);
void doConvertOfficeDocument(QString filename, Bytestream 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 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 doConvertPlaintext(QString filename, Bytestream header,
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble, bool BackHFlip, bool BackVFlip);
void additionalDocumentFormatsChanged();
void busyMessageChanged();
@ -85,54 +81,46 @@ signals:
public slots:
void print(QJsonObject attrs, QString file);
void onUrlChanged();
void MaybeGetStrings();
void MaybeGetIcon(bool retry=false);
void UpdateAdditionalDocumentFormats();
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);
void getStringsFinished(CURLcode res, Bytestream data);
void getImageFinished(CURLcode res, Bytestream data);
static void ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
void convertFailed(QString message);
private:
QUrl _url;
QUrl httpUrl();
void resolveUrl(QUrl& url);
QJsonObject opAttrs();
void adjustRasterSettings(QString filename, QString mimeType, QJsonObject& jobAttrs, PrintParameters& Params);
void adjustRasterSettings(QString documentFormat, QJsonObject& jobAttrs, quint32& HwResX, quint32& HwResY,
bool& BackHFlip, bool& BackVFlip);
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(IppMsg::Operation operation, QJsonObject opAttrs, QJsonObject jobAttrs=QJsonObject());
IppMsg mk_msg(QJsonObject opAttrs, QJsonObject jobAttrs=QJsonObject());
QJsonObject _attrs;
QJsonObject _jobAttrs;
QJsonArray _jobs;
QJsonObject _strings;
QImage _icon;
QStringList _additionalDocumentFormats;
QString _busyMessage;
QString _progress;
PrinterWorker* _worker;
QThread _workerThread;
ConvertWorker* _worker;
bool _iconRetried = false;
};
#endif // IPPPRINTER_H

View file

@ -1,5 +1,4 @@
#include "mimer.h"
#include <QDebug>
const QString Mimer::OctetStream = "application/octet-stream";
@ -12,22 +11,16 @@ 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::RasterFormats = {PWG, URF};
const QStringList Mimer::OfficeFormats = {DOC, DOCX, RTF, RTF_APP, ODT, PPT, PPTX, ODP};
const QStringList Mimer::OfficeFormats = {DOC, DOCX, RTF, RTF_APP, ODT};
Mimer::Mimer()
{
@ -53,14 +46,5 @@ Mimer* Mimer::instance()
}
QString Mimer::get_type(QString filename) {
QString type = _db.mimeTypeForFile(filename).name();
qDebug() << "MimeType:" << type;
if(type == PDF || type == Postscript || type == Plaintext || isImage(type) || isOffice(type))
{
return type;
}
else
{
return "";
}
return _db.mimeTypeForFile(filename).name();
}

View file

@ -24,17 +24,11 @@ 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);
@ -51,30 +45,20 @@ 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/") && ! isRaster(mimeType);
}
Q_INVOKABLE static bool isRaster(QString mimeType)
{
return RasterFormats.contains(mimeType);
return mimeType.startsWith("image/");
}
Q_INVOKABLE static bool isOffice(QString mimeType)
{

View file

@ -40,7 +40,7 @@ Overrider* Overrider::instance()
bool Overrider::apply(QJsonObject& attrs)
{
bool applied = false;
for(const QString& key : _overrides.keys())
foreach(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();
for(const QString& o_key : overrideden_data.keys())
foreach(const QString& o_key, overrideden_data.keys())
{
attrs.insert(o_key, overrideden_data[o_key]);
}

View file

@ -3,6 +3,208 @@
#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"},

View file

@ -1,812 +0,0 @@
#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));
}

View file

@ -1,72 +0,0 @@
#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

View file

@ -1,47 +0,0 @@
#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;
}

View file

@ -1,29 +0,0 @@
#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

View file

@ -1,21 +0,0 @@
#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);
}

View file

@ -1,21 +0,0 @@
#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

View file

@ -2,9 +2,7 @@
#include <QMutex>
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)
Settings::Settings() : _ignoreSslErrorsSetting("/apps/harbour-seaprint/settings/ignore-ssl-errors", this)
{
}
@ -39,23 +37,3 @@ 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();
}

View file

@ -11,18 +11,10 @@ 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();
@ -33,17 +25,10 @@ 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

43
src/svgprovider.cpp Normal file
View file

@ -0,0 +1,43 @@
#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;
}

28
src/svgprovider.h Normal file
View file

@ -0,0 +1,28 @@
#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