Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial release

  • Loading branch information...
commit 0c304192cdd1d3f559132b831e1be7f852e22b0c 0 parents
@SokoloffA SokoloffA authored
Showing with 9,330 additions and 0 deletions.
  1. +17 −0 .gitignore
  2. +16 −0 CMakeLists.txt
  3. +13 −0 cups/CMakeLists.txt
  4. +88 −0 cups/booklet
  5. +121 −0 gui/CMakeLists.txt
  6. +103 −0 gui/cmake/FindGhostscript.cmake
  7. +69 −0 gui/dbus.cpp
  8. +53 −0 gui/dbus.h
  9. BIN  gui/images/go-first-view.png
  10. BIN  gui/images/go-last-view.png
  11. BIN  gui/images/go-next-view.png
  12. BIN  gui/images/go-previous-view.png
  13. BIN  gui/images/printer.png
  14. +403 −0 gui/kernel/printer.cpp
  15. +113 −0 gui/kernel/printer.h
  16. +39 −0 gui/kernel/psconstants.h
  17. +543 −0 gui/kernel/psengine.cpp
  18. +96 −0 gui/kernel/psengine.h
  19. +269 −0 gui/kernel/psfile.cpp
  20. +119 −0 gui/kernel/psfile.h
  21. +334 −0 gui/kernel/psproject.cpp
  22. +168 −0 gui/kernel/psproject.h
  23. +195 −0 gui/kernel/pssheet.cpp
  24. +130 −0 gui/kernel/pssheet.h
  25. +169 −0 gui/main.cpp
  26. +512 −0 gui/mainwindow.cpp
  27. +98 −0 gui/mainwindow.h
  28. +280 −0 gui/mainwindow.ui
  29. +311 −0 gui/printersettings/printersettings.cpp
  30. +73 −0 gui/printersettings/printersettings.h
  31. +176 −0 gui/printersettings/printersettings.ui
  32. +421 −0 gui/psrender.cpp
  33. +66 −0 gui/psrender.h
  34. +164 −0 gui/psview.cpp
  35. +69 −0 gui/psview.h
  36. +9 −0 gui/resource.qrc
  37. +220 −0 gui/settings.cpp
  38. +74 −0 gui/settings.h
  39. +2,391 −0 gui/third-party/libspectre/ps.c
  40. +244 −0 gui/third-party/libspectre/ps.h
  41. +48 −0 gui/third-party/libspectre/spectre-macros.h
  42. +381 −0 gui/third-party/libspectre/spectre-utils.c
  43. +139 −0 gui/third-party/libspectre/spectre-utils.h
  44. +208 −0 gui/translations/booklet_ru.ts
  45. +4 −0 gui/translations/update.sh
  46. +212 −0 gui/widgets.cpp
  47. +101 −0 gui/widgets.h
  48. +71 −0 scripts/checkLicense.sh
17 .gitignore
@@ -0,0 +1,17 @@
+moc_*.cxx
+qrc_*.cxx
+cmake_install.cmake
+cmake_uninstall.cmake
+Makefile
+CMakeFiles
+CMakeCache.txt
+CPackConfig.cmake
+CPackSourceConfig.cmake
+lib*.so
+lib*.so.*
+ui_*.h
+*.qm
+*~
+*.autosave
+CMakeLists.txt.user*
+/build
16 CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required( VERSION 2.6 )
+
+project(booklet)
+
+set(GUI_DIR ${CMAKE_INSTALL_PREFIX}/lib/booklet)
+add_definitions(-DGUI_DIR=\"${GUI_DIR}\")
+
+set(GUI_PROGRAM ${GUI_DIR}/booklet)
+add_definitions(-DGUI_PROGRAM=\"${GUI_PROGRAM}\")
+
+set(MY_CUPS_URI "booklet:/")
+add_definitions(-DMY_CUPS_URI=\"${MY_CUPS_URI}\")
+
+add_subdirectory(cups)
+add_subdirectory(gui)
+
13 cups/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required( VERSION 2.6 )
+
+project(bokletCups)
+
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/booklet
+ ${CMAKE_CURRENT_BINARY_DIR}/booklet
+ @ONLY)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/booklet
+ DESTINATION /usr/lib/cups/backend
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
+)
88 cups/booklet
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+TMP_DIR="/tmp"
+
+CUPS_BACKEND_OK=0
+CUPS_BACKEND_FAILED=1
+
+# Have debug info in /var/log/cups/error_log:
+#set -x
+
+# Output "device discovery" information on stdout:
+if test "$#" = "0"; then
+ echo 'file @MY_CUPS_URI@ "Booklet Printer" "Booklet" "MFG:Generic;MDL:Booklet Printer;DES:Booklet Printer;CLS:PRINTER;CMD:POSTSCRIPT;"'
+ exit 0
+fi
+
+jobID=$1
+user=$2
+title=$3
+count=$4
+options=$5
+psFile=$6
+
+if [ -z ${psFile} ]; then
+ psFile=${TMP_DIR}/brint_in_file-${jobID}.ps
+ cat - > ${psFile}
+fi
+
+
+uid=$(id -u $user)
+
+if [ "${uid}" == "" ]; then
+ echo "ERROR: Can't found UID for user '$user'" >&2
+ exit $CUPS_BACKEND_FAILED
+fi
+
+
+echo "DEBUG: User $user" >&2
+echo "DEBUG: Uid $uid" >&2
+
+oldIfs=$IFS
+IFS=$(echo -en "\n")
+while read line; do
+ IFS="$oldIfs='"
+ read key value <<< $line;
+
+ case $key in
+ Session*)
+ if [ "$sesUid" = "$uid" -a "$sesActive" = "TRUE" -a "$sesDisplay" != "" ]; then
+ echo "DEBUG: Session=$line" >&2
+ echo "DEBUG: DISPLAY=$sesDisplay" >&2
+
+ xDisplay=$sesDisplay
+ break;
+ fi
+
+ unset sesUid
+ unset sesActive
+ unset sesADisplay
+ ;;
+
+ unix-user)
+ sesUid=$value
+ ;;
+
+ active)
+ sesActive=$value
+ ;;
+
+ x11-display)
+ sesDisplay=$value
+ ;;
+ esac
+done <<< $(ck-list-sessions | tac );
+
+
+if [ "${xDisplay}" == "" ]; then
+ echo "ERROR: Can't found session for user '$user' (UID: $uid)" >&2
+ exit $CUPS_BACKEND_FAILED
+fi
+
+chown ${user} ${psFile}
+export DISPLAY=${xDisplay}
+su -c "@GUI_PROGRAM@ -t \"$title\" -n $count $psFile" $user 0<&- &>/tmp/booklet-${user}.log &
+
+exit $CUPS_BACKEND_OK
+
+
121 gui/CMakeLists.txt
@@ -0,0 +1,121 @@
+cmake_minimum_required( VERSION 2.6 )
+
+project(bookletGui)
+
+set(HEADERS
+ kernel/psconstants.h
+ kernel/psfile.h
+ kernel/psproject.h
+ kernel/pssheet.h
+ kernel/psengine.h
+ kernel/printer.h
+ third-party/libspectre/ps.h
+ third-party/libspectre/spectre-utils.h
+ mainwindow.h
+ settings.h
+ widgets.h
+
+ dbus.h
+ printersettings/printersettings.h
+ psrender.h
+ psview.h
+)
+
+set(SOURCES
+ main.cpp
+ kernel/psfile.cpp
+ kernel/psproject.cpp
+ kernel/pssheet.cpp
+ kernel/psengine.cpp
+ kernel/printer.cpp
+ third-party/libspectre/ps.c
+ third-party/libspectre/spectre-utils.c
+ mainwindow.cpp
+ settings.cpp
+ widgets.cpp
+ dbus.cpp
+ printersettings/printersettings.cpp
+ psrender.cpp
+ psview.cpp
+)
+
+set (MOCS
+ kernel/psfile.h
+ kernel/psproject.h
+ mainwindow.h
+ widgets.h
+ dbus.h
+ printersettings/printersettings.h
+ psrender.h
+ psview.h
+)
+
+set(LIBRARIES
+)
+
+set(QT_USE_QTDBUS 1)
+
+set(UI_FILES
+ mainwindow.ui
+ printersettings/printersettings.ui
+)
+
+# QRC files .................
+set(RESOURCES
+ resource.qrc
+)
+
+# add translations ...
+file (GLOB TS_FILES
+ translations/*.ts
+)
+
+set(DATA_DIR
+ ${CMAKE_INSTALL_PREFIX}/share/booklet
+)
+
+set(TRANSLATIONS_DIR
+ "${DATA_DIR}/translations"
+)
+add_definitions(-DTRANSLATIONS_DIR=\"${TRANSLATIONS_DIR}\")
+
+
+############################################
+if (NOT CMAKE_BUILD_TYPE)
+ set ( CMAKE_BUILD_TYPE Release )
+endif (NOT CMAKE_BUILD_TYPE)
+set(CMAKE_BUILD_TYPE Debug)
+#if(CMAKE_COMPILER_IS_GNUCXX)
+# add_definitions (-Wall -pedantic)
+#endif()
+
+#*******************************************
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/third-party
+ ${CMAKE_CURRENT_SOURCE_DIR}/third-party/libspectre
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
+find_package(Qt4 REQUIRED)
+find_package(Ghostscript REQUIRED)
+
+include(${QT_USE_FILE})
+qt4_wrap_cpp(MOC_SOURCES ${MOCS})
+qt4_add_resources(QRC_SOURCES ${RESOURCES})
+qt4_wrap_ui(UI_HEADERS ${UI_FILES})
+qt4_add_translation(QM_FILES ${TS_FILES})
+
+
+add_executable(booklet ${HEADERS} ${SOURCES} ${MOC_SOURCES} ${QM_FILES} ${QRC_SOURCES} ${UI_HEADERS})
+target_link_libraries(booklet ${LIBRARIES} ${QT_LIBRARIES} ${GHOSTSCRIPT_LIBRARIES})
+
+install(FILES ${QM_FILES} DESTINATION ${TRANSLATIONS_DIR})
+install(TARGETS booklet RUNTIME DESTINATION ${GUI_DIR})
+
+
+
+
103 gui/cmake/FindGhostscript.cmake
@@ -0,0 +1,103 @@
+# +-----------------------------------------------------------------------------+
+# | Copyright (C) 2012 |
+# | Lars B"ahren (lbaehren@gmail.com) |
+# | |
+# | This program is free software; you can redistribute it and/or modify |
+# | it under the terms of the GNU General Public License as published by |
+# | the Free Software Foundation; either version 2 of the License, or |
+# | (at your option) any later version. |
+# | |
+# | This program is distributed in the hope that it will be useful, |
+# | but WITHOUT ANY WARRANTY; without even the implied warranty of |
+# | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+# | GNU General Public License for more details. |
+# | |
+# | You should have received a copy of the GNU General Public License |
+# | along with this program; if not, write to the |
+# | Free Software Foundation, Inc., |
+# | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
+# +-----------------------------------------------------------------------------+
+
+# - Check for the presence of GHOSTSCRIPT
+#
+# The following variables are set when GHOSTSCRIPT is found:
+# GHOSTSCRIPT_FOUND = Set to true, if all components of GHOSTSCRIPT have been found.
+# GHOSTSCRIPT_INCLUDES = Include path for the header files of GHOSTSCRIPT
+# GHOSTSCRIPT_LIBRARIES = Link these to use GHOSTSCRIPT
+# GHOSTSCRIPT_LFLAGS = Linker flags (optional)
+
+if (NOT GHOSTSCRIPT_FOUND)
+
+ if (NOT GHOSTSCRIPT_ROOT_DIR)
+ set (GHOSTSCRIPT_ROOT_DIR ${CMAKE_INSTALL_PREFIX})
+ endif (NOT GHOSTSCRIPT_ROOT_DIR)
+
+ ##_____________________________________________________________________________
+ ## Check for the header files
+
+ find_path (GHOSTSCRIPT_INCLUDES
+ NAMES ghostscript/gdevdsp.h ghostscript/iapi.h
+ HINTS ${GHOSTSCRIPT_ROOT_DIR} ${CMAKE_INSTALL_PREFIX}
+ PATH_SUFFIXES include
+ )
+
+ ##_____________________________________________________________________________
+ ## Check for the library
+
+ find_library (GHOSTSCRIPT_LIBRARIES gs
+ HINTS ${GHOSTSCRIPT_ROOT_DIR} ${CMAKE_INSTALL_PREFIX}
+ PATH_SUFFIXES lib
+ )
+
+ ##_____________________________________________________________________________
+ ## Check for the executable
+
+ find_program (GS_EXECUTABLE gs
+ HINTS ${GHOSTSCRIPT_ROOT_DIR} ${CMAKE_INSTALL_PREFIX}
+ PATH_SUFFIXES bin
+ )
+
+ find_program (EPS2EPS_EXECUTABLE eps2eps
+ HINTS ${GHOSTSCRIPT_ROOT_DIR} ${CMAKE_INSTALL_PREFIX}
+ PATH_SUFFIXES bin
+ )
+
+ find_program (DVIPDF_EXECUTABLE dvipdf
+ HINTS ${GHOSTSCRIPT_ROOT_DIR} ${CMAKE_INSTALL_PREFIX}
+ PATH_SUFFIXES bin
+ )
+
+ find_program (PS2PS2_EXECUTABLE ps2ps2
+ HINTS ${GHOSTSCRIPT_ROOT_DIR} ${CMAKE_INSTALL_PREFIX}
+ PATH_SUFFIXES bin
+ )
+
+ ##_____________________________________________________________________________
+ ## Actions taken when all components have been found
+
+ find_package_handle_standard_args (GHOSTSCRIPT DEFAULT_MSG GHOSTSCRIPT_LIBRARIES GHOSTSCRIPT_INCLUDES GS_EXECUTABLE)
+
+ if (GHOSTSCRIPT_FOUND)
+ if (NOT GHOSTSCRIPT_FIND_QUIETLY)
+ message (STATUS "Found components for GHOSTSCRIPT")
+ message (STATUS "GHOSTSCRIPT_ROOT_DIR = ${GHOSTSCRIPT_ROOT_DIR}")
+ message (STATUS "GHOSTSCRIPT_INCLUDES = ${GHOSTSCRIPT_INCLUDES}")
+ message (STATUS "GHOSTSCRIPT_LIBRARIES = ${GHOSTSCRIPT_LIBRARIES}")
+ endif (NOT GHOSTSCRIPT_FIND_QUIETLY)
+ else (GHOSTSCRIPT_FOUND)
+ if (GHOSTSCRIPT_FIND_REQUIRED)
+ message (FATAL_ERROR "Could not find GHOSTSCRIPT!")
+ endif (GHOSTSCRIPT_FIND_REQUIRED)
+ endif (GHOSTSCRIPT_FOUND)
+
+ ##_____________________________________________________________________________
+ ## Mark advanced variables
+
+ mark_as_advanced (
+ GHOSTSCRIPT_ROOT_DIR
+ GHOSTSCRIPT_INCLUDES
+ GHOSTSCRIPT_LIBRARIES
+ GS_EXECUTABLE
+ )
+
+endif (NOT GHOSTSCRIPT_FOUND)
69 gui/dbus.cpp
@@ -0,0 +1,69 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#include "dbus.h"
+#include "kernel/psproject.h"
+
+#include <QDBusConnection>
+#include <QDBusInterface>
+#include <QDBusMessage>
+#include <QDebug>
+
+/************************************************
+
+ ************************************************/
+DBusProjectAdaptor::DBusProjectAdaptor(PsProject *project) :
+ QDBusAbstractAdaptor(project),
+ mProject(project)
+{
+}
+
+
+/************************************************
+
+ ************************************************/
+bool DBusProjectAdaptor::openFileInExisting(const QString &fileName)
+{
+ QDBusInterface remote("org.bprint", "/Project");
+ if (!remote.isValid())
+ return false;
+
+ QDBusMessage res = remote.call("addFile", fileName);
+ if (res.errorName().isEmpty())
+ return true;
+
+ qWarning() << res.errorMessage();
+ return false;
+}
+
+
+/************************************************
+
+ ************************************************/
+void DBusProjectAdaptor::addFile(const QString &fileName)
+{
+ mProject->addFile(fileName);
+}
53 gui/dbus.h
@@ -0,0 +1,53 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#ifndef DBUS_H
+#define DBUS_H
+
+
+#include <QDBusAbstractAdaptor>
+#include <QApplication>
+
+class PsProject;
+
+class DBusProjectAdaptor : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.bprint")
+public:
+ DBusProjectAdaptor(PsProject *project);
+
+ static bool openFileInExisting(const QString &fileName);
+
+public slots:
+ void addFile(const QString &fileName);
+
+private:
+ PsProject *mProject;
+};
+
+
+#endif // DBUS_H
BIN  gui/images/go-first-view.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  gui/images/go-last-view.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  gui/images/go-next-view.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  gui/images/go-previous-view.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  gui/images/printer.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
403 gui/kernel/printer.cpp
@@ -0,0 +1,403 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#include "printer.h"
+#include "settings.h"
+#include "kernel/psconstants.h"
+
+#include <QProcess>
+#include <QSharedData>
+#include <cups/cups.h>
+#include <cups/ppd.h>
+#include <QFileInfo>
+#include <QDebug>
+
+#define CUPS_DEVICE_URI "device-uri"
+
+#ifndef CUPS_SIDES
+# define CUPS_SIDES "sides"
+#endif
+
+#ifndef CUPS_SIDES_ONE_SIDED
+# define CUPS_SIDES_ONE_SIDED "one-sided"
+#endif
+
+#ifndef CUPS_SIDES_TWO_SIDED_PORTRAIT
+# define CUPS_SIDES_TWO_SIDED_PORTRAIT "two-sided-long-edge"
+#endif
+
+#ifndef CUPS_SIDES_TWO_SIDED_LANDSCAPE
+# define CUPS_SIDES_TWO_SIDED_LANDSCAPE "two-sided-short-edge"
+#endif
+
+
+/************************************************
+
+ ************************************************/
+qreal toUnit(qreal value, Printer::Unit unit)
+{
+ switch (unit)
+ {
+ case Printer::Point:
+ return value;
+
+ case Printer::Millimeter:
+ return value * PT_TO_MM;
+ }
+}
+
+
+/************************************************
+
+ ************************************************/
+qreal fromUnit(qreal value, Printer::Unit unit)
+{
+ switch (unit)
+ {
+ case Printer::Point:
+ return value;
+
+ case Printer::Millimeter:
+ return value * MM_TO_PT;
+ }
+}
+
+
+/************************************************
+
+ ************************************************/
+Printer::Printer()
+{
+ init();
+}
+
+
+/************************************************
+
+ ************************************************/
+Printer::Printer(QPrinterInfo printerInfo):
+ mPrinterInfo(printerInfo)
+{
+ init();
+ initFromCups();
+ readSettings();
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::init()
+{
+ mDeviceUri = "";
+
+ mDuplex = false;
+ mPaperSize = QSizeF(A4_WIDTH_PT, A4_HEIGHT_PT);
+ mLeftMargin = 0;
+ mRightMargin = 0;
+ mTopMargin = 0;
+ mBottomMargin = 0;
+ mInternalMargin = 14;
+ mDrawBorder = false;
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::initFromCups()
+{
+ cups_dest_t *dests;
+ int num_dests = cupsGetDests(&dests);
+ cups_dest_t *dest = cupsGetDest(mPrinterInfo.printerName().toLocal8Bit().data(),
+ 0, num_dests, dests);
+
+#if 0
+ qDebug() << "**" << mPrinterInfo.printerName() << "*******************";
+ for (int j=0; j<dest->num_options; ++j)
+ qDebug() << " *" << dest->options[j].name << dest->options[j].value;
+#endif
+
+
+ mDeviceUri = cupsGetOption(CUPS_DEVICE_URI, dest->num_options, dest->options);
+ QString duplexStr = cupsGetOption(CUPS_SIDES, dest->num_options, dest->options);
+ mDuplex = (duplexStr != CUPS_SIDES_ONE_SIDED);
+
+ // Read values from PPD
+ // The returned filename is stored in a static buffer
+ const char * ppdFile = cupsGetPPD(mPrinterInfo.printerName().toLocal8Bit().data());
+ ppd_file_t *ppd = ppdOpenFile(ppdFile);
+ if (ppd)
+ {
+ ppdMarkDefaults(ppd);
+
+ ppd_size_t *size = ppdPageSize(ppd, 0);
+ if (size)
+ {
+ mPaperSize = QSizeF(size->width, size->length);
+ mLeftMargin = size->left;
+ mRightMargin = size->width - size->right;
+ mTopMargin = size->length - size->top;
+ mBottomMargin = size->bottom;
+ }
+ ppdClose(ppd);
+ }
+ QFile::remove(ppdFile);
+
+
+ bool ok;
+ int n;
+
+ n = QString(cupsGetOption("page-left", dest->num_options, dest->options)).toInt(&ok);
+ if (ok)
+ mLeftMargin = n;
+
+ n = QString(cupsGetOption("page-right", dest->num_options, dest->options)).toInt(&ok);
+ if (ok)
+ mRightMargin = n;
+
+ n = QString(cupsGetOption("page-top", dest->num_options, dest->options)).toInt(&ok);
+ if (ok)
+ mTopMargin = n;
+
+ n = QString(cupsGetOption("page-bottom", dest->num_options, dest->options)).toInt(&ok);
+ if (ok)
+ mBottomMargin = n;
+
+ cupsFreeDests(num_dests, dests);
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::readSettings()
+{
+ mDuplex = settings->printerDuplex(mPrinterInfo.printerName(), mDuplex);
+ mDrawBorder = settings->printerBorder(mPrinterInfo.printerName(), mDrawBorder);
+ mLeftMargin = settings->printerMargin(mPrinterInfo.printerName(), "Left", mLeftMargin);
+ mRightMargin = settings->printerMargin(mPrinterInfo.printerName(), "Right", mRightMargin);
+ mTopMargin = settings->printerMargin(mPrinterInfo.printerName(), "Top", mTopMargin);
+ mBottomMargin = settings->printerMargin(mPrinterInfo.printerName(), "Bottom", mBottomMargin);
+ mInternalMargin = settings->printerMargin(mPrinterInfo.printerName(), "Internal", mInternalMargin);
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::saveSettings()
+{
+ settings->setPrinterDuplex(mPrinterInfo.printerName(), mDuplex);
+ settings->setPrinterBorder(mPrinterInfo.printerName(), mDrawBorder);
+ settings->setPrinterMargin(mPrinterInfo.printerName(), "Left", mLeftMargin);
+ settings->setPrinterMargin(mPrinterInfo.printerName(), "Right", mRightMargin);
+ settings->setPrinterMargin(mPrinterInfo.printerName(), "Top", mTopMargin);
+ settings->setPrinterMargin(mPrinterInfo.printerName(), "Bottom", mBottomMargin);
+ settings->setPrinterMargin(mPrinterInfo.printerName(), "Internal", mInternalMargin);
+}
+
+
+/************************************************
+
+ ************************************************/
+QString Printer::printerName() const
+{
+ return mPrinterInfo.printerName();
+}
+
+
+/************************************************
+
+ ************************************************/
+QSizeF Printer::paperSize(Printer::Unit unit) const
+{
+ return QSizeF(toUnit(mPaperSize.width(), unit),
+ toUnit(mPaperSize.height(), unit));
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::setPaperSize(const QSizeF &paperSize, Printer::Unit unit)
+{
+ mPaperSize.setWidth( fromUnit(paperSize.width(), unit));
+ mPaperSize.setHeight(fromUnit(paperSize.height(), unit));
+}
+
+
+/************************************************
+
+ ************************************************/
+QRectF Printer::paperRect(Printer::Unit unit) const
+{
+ return QRectF(QPointF(0, 0), paperSize(unit));
+}
+
+
+/************************************************
+
+ ************************************************/
+QRectF Printer::pageRect(Printer::Unit unit) const
+{
+ return paperRect(unit).adjusted(
+ mLeftMargin,
+ mTopMargin,
+ -mRightMargin,
+ -mBottomMargin);
+}
+
+
+/************************************************
+
+ ************************************************/
+qreal Printer::leftMargin(Printer::Unit unit)
+{
+ return toUnit(mLeftMargin, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::setLeftMargin(qreal value, Printer::Unit unit)
+{
+ mLeftMargin = fromUnit(value, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+qreal Printer::rightMargin(Printer::Unit unit)
+{
+ return toUnit(mRightMargin, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::setRightMargin(qreal value, Printer::Unit unit)
+{
+ mRightMargin = fromUnit(value, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+qreal Printer::topMargin(Printer::Unit unit)
+{
+ return toUnit(mTopMargin, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::setTopMargin(qreal value, Printer::Unit unit)
+{
+ mTopMargin = fromUnit(value, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+qreal Printer::bottomMargin(Printer::Unit unit)
+{
+ return toUnit(mBottomMargin, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::setBottomMargin(qreal value, Printer::Unit unit)
+{
+ mBottomMargin = fromUnit(value, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+qreal Printer::internalMarhin(Printer::Unit unit)
+{
+ return toUnit(mInternalMargin, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::setInternalMargin(qreal value, Printer::Unit unit)
+{
+ mInternalMargin = fromUnit(value, unit);
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::setDrawBorder(bool value)
+{
+ mDrawBorder = value;
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::setDuplex(bool duplex)
+{
+ mDuplex = duplex;
+}
+
+
+/************************************************
+
+ ************************************************/
+void Printer::print(const QString &fileName, const QString &jobName, int numCopies)
+{
+ QStringList args;
+ args << "-P" << printerName(); // Prints files to the named printer.
+ args << "-#" << QString("%1").arg(numCopies);// Sets the number of copies to print
+ //args << "-r"; // Specifies that the named print files should be deleted after printing them.
+ args << "-T" << jobName; // Sets the job name.
+
+ args << fileName;
+
+ //qDebug() << args;
+ //QProcess::startDetached("okular " + fileName);
+
+ QProcess proc;
+ proc.start("lpr", args);
+ proc.waitForFinished();
+}
+
113 gui/kernel/printer.h
@@ -0,0 +1,113 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#ifndef PRINTER_H
+#define PRINTER_H
+
+#include <QObject>
+#include <QList>
+#include <QString>
+#include <QPrinterInfo>
+#include <QExplicitlySharedDataPointer>
+
+class Printer
+{
+public:
+
+ enum Unit {
+ Millimeter = 0,
+ Point = 1
+ //Inch = 2
+ };
+
+ //enum PaperSize { A4, B5, Letter, Legal, Executive,
+ // A0, A1, A2, A3, A5, A6, A7, A8, A9, B0, B1,
+ // B10, B2, B3, B4, B6, B7, B8, B9, C5E, Comm10E,
+ // DLE, Folio, Ledger, Tabloid, Custom, NPageSize = Custom, NPaperSize = Custom };
+
+ Printer();
+ explicit Printer(QPrinterInfo printerInfo);
+
+ QString printerName() const;
+ QPrinterInfo printerInfo() const { return mPrinterInfo; }
+
+
+ //PaperSize paperSize() const;
+ QSizeF paperSize(Unit unit) const;
+
+ //void setPaperSize(PaperSize newPaperSize);
+ void setPaperSize(const QSizeF & paperSize, Unit unit);
+
+ QRectF paperRect(Unit unit=Point) const;
+
+ QRectF pageRect(Unit unit=Point) const;
+
+ qreal leftMargin(Unit unit=Point);
+ void setLeftMargin(qreal value, Unit unit);
+
+ qreal rightMargin(Unit unit=Point);
+ void setRightMargin(qreal value, Unit unit);
+
+ qreal topMargin(Unit unit=Point);
+ void setTopMargin(qreal value, Unit unit);
+
+ qreal bottomMargin(Unit unit=Point);
+ void setBottomMargin(qreal value, Unit unit);
+
+ qreal internalMarhin(Unit unit=Point);
+ void setInternalMargin(qreal value, Unit unit);
+
+ bool drawBorder() const { return mDrawBorder; }
+ void setDrawBorder(bool value);
+
+ bool duplex() const { return mDuplex; }
+ void setDuplex(bool duplex);
+
+ void print(const QString &fileName, const QString &jobName, int numCopies);
+
+ QString deviceUri() const { return mDeviceUri; }
+
+ void readSettings();
+ void saveSettings();
+
+private:
+ QPrinterInfo mPrinterInfo;
+ QString mDeviceUri; QSizeF mPaperSize;
+ qreal mLeftMargin;
+ qreal mRightMargin;
+ qreal mTopMargin;
+ qreal mBottomMargin;
+ qreal mInternalMargin;
+ bool mDuplex;
+ bool mDrawBorder;
+
+ void init();
+ void initFromCups();
+
+};
+
+
+#endif // PRINTER_H
39 gui/kernel/psconstants.h
@@ -0,0 +1,39 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#ifndef PSCONSTANTS_H
+#define PSCONSTANTS_H
+
+#define A4_HEIGHT_MM 297
+#define A4_HEIGHT_PT 842
+#define A4_WIDTH_MM 210
+#define A4_WIDTH_PT 595
+
+#define MM_TO_PT (A4_HEIGHT_PT * 1.0 / A4_HEIGHT_MM)
+#define PT_TO_MM (A4_HEIGHT_MM * 1.0 / A4_HEIGHT_PT)
+
+
+#endif // PSCONSTANTS_H
543 gui/kernel/psengine.cpp
@@ -0,0 +1,543 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#include "psengine.h"
+#include "pssheet.h"
+#include "printer.h"
+
+
+#include <math.h>
+#include <QDebug>
+#include <QRect>
+
+
+/************************************************
+
+ ************************************************/
+PsEngine::PsEngine(PsProject *project) :
+ mProject(project)
+{
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsEngine::fillPreviewSheets(QList<PsSheet *> *sheets)
+{
+ fillSheets(sheets);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsEngine::writeMatrixText(double xOffset, double yOffset, int rotate, double scale, QTextStream *out)
+{
+ *out << "userdict/PStoPSsaved save put\n";
+ *out << "PStoPSmatrix setmatrix\n";
+ *out << QString("%1 %2 translate\n").arg(xOffset, 0, 'f').arg(yOffset, 0, 'f');
+ *out << QString("%1 rotate\n").arg(rotate);
+ *out << QString("%1 dup scale\n").arg(scale, 0, 'f');
+ *out << "userdict/PStoPSmatrix matrix currentmatrix put\n";
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsEngine::writeClipText(double width, double height, QTextStream *out)
+{
+ if (width > 0 && height > 0)
+ {
+ *out << "userdict/PStoPSclip{0 0 moveto\n";
+ *out << QString(" %1 0 rlineto 0 %2 rlineto -%1 0 rlineto\n").arg(width, 0, 'f').arg(height, 0, 'f');
+ *out << " closepath}put initclip\n";
+ }
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsEngine::writeBorderText(double borderWidth, QTextStream *out)
+{
+ if (borderWidth > 0)
+ *out << QString("gsave clippath 0 setgray %1 setlinewidth stroke grestore\n")
+ .arg(borderWidth);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsEngine::writeProcSetText(QTextStream *out)
+{
+ *out << "%%BeginProcSet: PStoPS 1 15\n";
+ *out << "userdict begin\n";
+ *out << "[/showpage/erasepage/copypage]{dup where{pop dup load\n";
+ *out << " type/operatortype eq{ /PStoPSenablepage cvx 1 index load 1 array astore cvx {} bind /ifelse cvx 4 array astore cvx def}{pop}ifelse}{pop}ifelse}forall /PStoPSenablepage true def\n";
+ *out << "[/letter/legal/executivepage/a4/a4small/b5/com10envelope\n";
+ *out << " /monarchenvelope/c5envelope/dlenvelope/lettersmall/note\n";
+ *out << " /folio/quarto/a5]{dup where{dup wcheck{exch{}put}\n";
+ *out << " {pop{}def}ifelse}{pop}ifelse}forall\n";
+ *out << "/setpagedevice {pop}bind 1 index where{dup wcheck{3 1 roll put}\n";
+ *out << " {pop def}ifelse}{def}ifelse\n";
+ *out << "/PStoPSmatrix matrix currentmatrix def\n";
+ *out << "/PStoPSxform matrix def/PStoPSclip{clippath}def\n";
+ *out << "/defaultmatrix{PStoPSmatrix exch PStoPSxform exch concatmatrix}bind def\n";
+ *out << "/initmatrix{matrix defaultmatrix setmatrix}bind def\n";
+ *out << "/initclip[{matrix currentmatrix PStoPSmatrix setmatrix\n";
+ *out << " [{currentpoint}stopped{$error/newerror false put{newpath}}\n";
+ *out << " {/newpath cvx 3 1 roll/moveto cvx 4 array astore cvx}ifelse]\n";
+ *out << " {[/newpath cvx{/moveto cvx}{/lineto cvx}\n";
+ *out << " {/curveto cvx}{/closepath cvx}pathforall]cvx exch pop}\n";
+ *out << " stopped{$error/errorname get/invalidaccess eq{cleartomark\n";
+ *out << " $error/newerror false put cvx exec}{stop}ifelse}if}bind aload pop\n";
+ *out << " /initclip dup load dup type dup/operatortype eq{pop exch pop}\n";
+ *out << " {dup/arraytype eq exch/packedarraytype eq or\n";
+ *out << " {dup xcheck{exch pop aload pop}{pop cvx}ifelse}\n";
+ *out << " {pop cvx}ifelse}ifelse\n";
+ *out << " {newpath PStoPSclip clip newpath exec setmatrix} bind aload pop]cvx def\n";
+ *out << "/initgraphics{initmatrix newpath initclip 1 setlinewidth\n";
+ *out << " 0 setlinecap 0 setlinejoin []0 setdash 0 setgray\n";
+ *out << " 10 setmiterlimit}bind def\n";
+ *out << "end\n";
+ *out << "%%EndProcSet\n";
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsEngine::writePage(const PsProjectPage *page, QTextStream *out)
+{
+ if (page)
+ mProject->psFile()->writePageBody(page->begin(), page->end(), out);
+ else
+ *out << "showpage\n";
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsEngine::writeMatrix(const PsSheetPageSpec &spec, QTextStream *out)
+{
+ QPointF basePoint;
+ switch (spec.rotate())
+ {
+ case PsSheetPageSpec::NoRotate:
+ basePoint = spec.rect().topLeft();
+ break;
+
+ case PsSheetPageSpec::Rotate90:
+ basePoint = spec.rect().topRight();
+ break;
+
+ case PsSheetPageSpec::Rotate180:
+ basePoint = spec.rect().bottomRight();
+ break;
+
+ case PsSheetPageSpec::Rotate270:
+ basePoint = spec.rect().bottomLeft();
+ break;
+ }
+
+ writeMatrixText(
+ /* xOffset */ basePoint.x(),
+ /* yOffset */ basePoint.y(),
+ /* rotate */ spec.rotate(),
+ /* scale */ spec.scale(),
+ out);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsEngine::writeSheet(const PsSheet &sheet, QTextStream *out)
+{
+ int lastPage = sheet.count()-1;
+
+ for (int i=0; i<sheet.count(); ++i)
+ {
+ const PsProjectPage *page = sheet.page(i);
+ const PsSheetPageSpec spec = sheet.pageSpec(i);
+
+ writeMatrix(spec, out);
+
+ if (page)
+ {
+ writeClipText(page->rect().width(), page->rect().height(), out);
+ if (project()->mPrinter->drawBorder())
+ writeBorderText(1, out);
+ }
+ if (i != lastPage)
+ *out << "/PStoPSenablepage false def\n";
+
+ *out << "PStoPSxform concat\n";
+
+
+ writePage(page, out);
+ *out << "PStoPSsaved restore\n";
+ }
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsEngine::writeDocument(const QList<const PsSheet*> &sheets, QTextStream *out)
+{
+ *out << "%!PS-Adobe-3.0\n";
+ *out << "%%BoundingBox: (atend)\n";
+ *out << "%%Creator: (bprint)\n";
+ *out << "%%LanguageLevel: 2\n";
+ *out << QString("%%Pages: %1\n").arg(sheets.count());
+ *out << "%%EndComments\n";
+
+ writeProcSetText(out);
+
+ mProject->psFile()->writeFilePart(mProject->psFile()->prologPos(), out);
+ mProject->psFile()->writeFilePart(mProject->psFile()->setupPos(), out);
+
+ int n=1;
+ foreach(const PsSheet *sheet, sheets)
+ {
+ *out << QString("%%Page: %1\n").arg(n++);
+ writeSheet(*sheet, out);
+ }
+ mProject->psFile()->writeFilePart(mProject->psFile()->trailerPos(), out);
+}
+
+
+/************************************************
+
+ ************************************************/
+EngineNUp::EngineNUp(PsProject *project, int pageCountVert, int pageCountHoriz):
+ PsEngine(project),
+ mPageCountVert(pageCountVert),
+ mPageCountHoriz(pageCountHoriz)
+{
+ mRotate = mPageCountVert != mPageCountHoriz;
+}
+
+
+/************************************************
+
+ ************************************************/
+void EngineNUp::fillSheets(QList<PsSheet *> *sheets)
+{
+ PsSheet *sheet;
+
+ int pps = mPageCountVert * mPageCountHoriz;
+
+ int s=0;
+ int i=0;
+ while (i < mProject->pageCount())
+ {
+ sheet = new PsSheet(mProject, pps);
+ sheet->setSheetNum(sheets->count());
+ sheet->setHint(PsSheet::HintLandscapePreview, mRotate);
+
+ for (int j=0; j<pps; ++j)
+ {
+ if (i<mProject->pageCount())
+ {
+ PsProjectPage *page = mProject->page(i);
+ sheet->setPage(j, page);
+ sheet->setPageSpec(j, pageSpecForPage(page, j));
+ }
+ ++i;
+ }
+ sheets->append(sheet);
+ }
+}
+
+
+/************************************************
+ Landscape = true
+ +------------------+
+ | ....... ....... |
+ | : : : : |
+ | : 0 : : 1 : |
+ | : : : : |
+ | ....... ....... |
+ +------------------+
+
+ Landscape = false
+ +------------------+
+ | ....... ....... |
+ | : : : : |
+ | : 0 : : 1 : |
+ | : : : : |
+ | ....... ....... |
+ | ....... ....... |
+ | : : : : |
+ | : 2 : : 3 : |
+ | : : : : |
+ | ....... ....... |
+ +------------------+
+
+ ************************************************/
+PsSheetPageSpec EngineNUp::pageSpecForPage(const PsProjectPage *page, int pageNumOnSheet)
+{
+ Printer *printer = mProject->printer();
+ const qreal m = printer->internalMarhin();
+ const QRectF printerRect = printer->pageRect();
+
+ QRectF pageRect;
+ pageRect.setWidth((printerRect.width() - m * (mPageCountHoriz - 1)) / mPageCountHoriz);
+ pageRect.setHeight((printerRect.height() - m * (mPageCountVert - 1)) / mPageCountVert);
+
+ qreal scale;
+ PsSheetPageSpec::Rotation rotation;
+
+ int col = pageNumOnSheet % mPageCountHoriz;
+ int row = pageNumOnSheet / mPageCountHoriz;
+
+ if (mRotate)
+ {
+ scale = qMin(
+ pageRect.width() / page->rect().height(),
+ pageRect.height() / page->rect().width());
+
+ pageRect.setWidth(page->rect().height() * scale);
+ pageRect.setHeight(page->rect().width() * scale);
+ rotation = PsSheetPageSpec::Rotate90;
+ }
+ else
+ {
+ scale = qMin(
+ pageRect.width() / page->rect().width(),
+ pageRect.height() / page->rect().height());
+
+ pageRect.setWidth(page->rect().width() * scale);
+ pageRect.setHeight(page->rect().height() * scale);
+ rotation = PsSheetPageSpec::NoRotate;
+ row = mPageCountVert - 1 - row;
+ }
+
+ QRectF r = printerRect.adjusted(-m * 0.5 , -m * 0.5, m * 0.5, m * 0.5);
+ qreal x = r.left() + (r.width() / (mPageCountHoriz * 2.0 ) * (col * 2.0 + 1));
+ qreal y = r.top() + (r.height() / (mPageCountVert * 2.0 ) * (row * 2.0 + 1));
+
+ pageRect.moveCenter(QPointF(x, y));
+ return PsSheetPageSpec(pageRect, scale, rotation);
+}
+
+
+/************************************************
+
+ ************************************************/
+void EngineBooklet::fillSheets(QList<PsSheet *> *sheets)
+{
+ fillSheetsForBook(0, mProject->pageCount(), sheets);
+}
+
+
+/************************************************
+
+ +-----------+ +-----------+
+ | : | | : |
+ | N : 0 | | 1 : N-1 |
+ | : | | : |
+ +-----------+ +-----------+
+ sheet 0 sheet 1
+
+ +-----------+ +-----------+
+ | : | | : |
+ | N-3 : 3 | | 4 : N-4 |
+ | : | | : |
+ +-----------+ +-----------+
+ sheet 2 sheet 3
+ ...
+ ************************************************/
+void EngineBooklet::fillSheetsForBook(int bookStart, int bookLength, QList<PsSheet *> *sheets)
+{
+ int cnt = ceil(bookLength / 4.0 ) * 4;
+ int n;
+ PsSheet *sheet;
+
+ for (int i = 0; i < cnt / 2; i+=2)
+ {
+ // Sheet 0 **************************
+ sheet = new PsSheet(mProject, 2);
+ sheet->setHints(PsSheet::HintDrawFold | PsSheet::HintLandscapePreview);
+ sheet->setSheetNum(sheets->count());
+ sheets->append(sheet);
+
+ n = (cnt - 1) - i;
+ if (n < bookLength)
+ {
+ PsProjectPage *page = mProject->page(n + bookStart);
+ sheet->setPage(0, page);
+ sheet->setPageSpec(0, pageSpecForPage(page, 0));
+ }
+
+
+ n = i;
+ if (n < bookLength)
+ {
+ PsProjectPage *page = mProject->page(n + bookStart);
+ sheet->setPage(1, page);
+ sheet->setPageSpec(1, pageSpecForPage(page, 1));
+ }
+
+
+ // Sheet 0 **************************
+ sheet = new PsSheet(mProject, 2);
+ sheet->setHints(PsSheet::HintDrawFold | PsSheet::HintLandscapePreview);
+ sheet->setSheetNum(sheets->count());
+ sheets->append(sheet);
+
+ n = i + 1;
+ if (n < bookLength)
+ {
+ PsProjectPage *page = mProject->page(n + bookStart);
+ sheet->setPage(0, page);
+ sheet->setPageSpec(0, pageSpecForPage(page, 0));
+ }
+
+ n = (cnt - 1) - (i + 1);
+ if (n < bookLength)
+ {
+ PsProjectPage *page = mProject->page(n + bookStart);
+ sheet->setPage(1, page);
+ sheet->setPageSpec(1, pageSpecForPage(page, 1));
+ }
+ }
+}
+
+
+/************************************************
+
+ ************************************************/
+void EngineBooklet::fillPreviewSheets(QList<PsSheet *> *sheets)
+{
+ fillPreviewSheetsForBook(0, mProject->pageCount(), sheets);
+ if (sheets->count() > 1)
+ {
+ sheets->first()->setHint(PsSheet::HintOnlyRight, true);
+ sheets->last()->setHint(PsSheet::HintOnlyLeft, true);
+ }
+}
+
+
+/************************************************
+ : - - +-----+ +-----------+
+ : | | | : |
+ : -1 | 0 | | 1 : 2 |
+ : | | | : |
+ : - - +-----+ +-----------+
+ sheet 0 sheet 1
+
+ +-----------+ +-----+ - - :
+ | : | | | :
+ | 3 : 4 | | 5 | :
+ | : | | | :
+ +-----------+ +-----+ - - :
+ sheet 2 sheet 3
+
+ ************************************************/
+void EngineBooklet::fillPreviewSheetsForBook(int bookStart, int bookLength, QList<PsSheet *> *sheets)
+{
+ int cnt = ceil(bookLength / 4.0 ) * 4;
+
+ PsSheet *sheet;
+ for (int i = -1; i < cnt; i+=2)
+ {
+ sheet = new PsSheet(mProject, 2);
+ sheet->setHints(PsSheet::HintDrawFold | PsSheet::HintLandscapePreview);
+ sheet->setSheetNum(sheets->count());
+ sheets->append(sheet);
+ if (i>-1)
+ {
+ if (i<mProject->pageCount())
+ {
+ PsProjectPage *page = mProject->page(i + bookStart);
+ sheet->setPage(0, page);
+ sheet->setPageSpec(0, pageSpecForPage(page, 0));
+ }
+ }
+
+ if (i+1<cnt)
+ {
+ if (i+1<mProject->pageCount())
+ {
+ PsProjectPage *page = mProject->page(i + 1 + bookStart);
+ sheet->setPage(1, page);
+ sheet->setPageSpec(1, pageSpecForPage(page, 1));
+ }
+ }
+ }
+}
+
+
+/************************************************
+ +---------------------------+
+ | ....................... |
+ | : : |
+ | : : |
+ | : : |
+ | : Page 0 : |
+ | : : |
+ | : : |
+ | ....................... |
+ | : : |
+ | : : |
+ | : : |
+ | : Page 1 : |
+ | : : |
+ | : : |
+ | ....................... |
+ +---------------------------+
+ ************************************************/
+PsSheetPageSpec EngineBooklet::pageSpecForPage(const PsProjectPage *page, int pageNumOnSheet)
+{
+ Printer *printer = mProject->printer();
+ QRectF printerRect = printer->pageRect();
+
+ double scale = qMin(
+ 1.0 * printerRect.width() / page->rect().height(),
+ (0.5 * printerRect.height() - printer->internalMarhin()) / page->rect().width()
+ );
+
+ QRectF pageRect= QRect(0, 0, page->rect().height() * scale, page->rect().width() * scale);
+ pageRect.moveCenter(printerRect.center());
+
+ if (pageNumOnSheet == 0)
+ pageRect.moveBottom(printerRect.center().y() - printer->internalMarhin());
+ else
+ pageRect.moveTop(printerRect.center().y() + printer->internalMarhin());
+
+ return PsSheetPageSpec(pageRect, scale, PsSheetPageSpec::Rotate90);
+}
+
+
+
+
96 gui/kernel/psengine.h
@@ -0,0 +1,96 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#ifndef PSENGINE_H
+#define PSENGINE_H
+
+#include "kernel/psproject.h"
+
+#include <QObject>
+#include <QTextStream>
+
+
+class PsSheet;
+
+class PsEngine
+{
+public:
+ explicit PsEngine(PsProject *project);
+ virtual ~PsEngine() {}
+
+ PsProject *project() const { return mProject; }
+
+ virtual void fillSheets(QList<PsSheet*> *sheets) = 0;
+ virtual void fillPreviewSheets(QList<PsSheet*> *sheets);
+
+ void writeDocument(const QList<const PsSheet *> &sheets, QTextStream *out);
+
+protected:
+ // Helper functions
+ void writeMatrixText(double xOffset, double yOffset, int rotate, double scale, QTextStream *out);
+ void writeClipText(double width, double height, QTextStream *out);
+ void writeBorderText(double borderWidth, QTextStream *out);
+ void writeProcSetText(QTextStream *out);
+ void writePage(const PsProjectPage *page, QTextStream *out);
+
+ void writeMatrix(const PsSheetPageSpec &spec, QTextStream *out);
+ void writeSheet(const PsSheet &sheet, QTextStream *out);
+
+protected:
+ PsProject *mProject;
+};
+
+
+class EngineNUp: public PsEngine
+{
+public:
+ explicit EngineNUp(PsProject *project, int pageCountVert, int pageCountHoriz);
+
+ void fillSheets(QList<PsSheet *> *sheets);
+
+private:
+ PsSheetPageSpec pageSpecForPage(const PsProjectPage *page, int pageNumOnSheet);
+ int mPageCountVert;
+ int mPageCountHoriz;
+ bool mRotate;
+};
+
+
+class EngineBooklet: public PsEngine
+{
+public:
+ explicit EngineBooklet(PsProject *project): PsEngine(project) {}
+
+ void fillSheets(QList<PsSheet *> *sheets);
+ void fillPreviewSheets(QList<PsSheet*> *sheets);
+
+private:
+ void fillSheetsForBook(int bookStart, int bookLength, QList<PsSheet *> *sheets);
+ void fillPreviewSheetsForBook(int bookStart, int bookLength, QList<PsSheet *> *sheets);
+ PsSheetPageSpec pageSpecForPage(const PsProjectPage *page, int pageNumOnSheet);
+};
+
+#endif // PSENGINE_H
269 gui/kernel/psfile.cpp
@@ -0,0 +1,269 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#include "psfile.h"
+
+#include <QTextStream>
+#include <QDebug>
+#include <QStringList>
+#include "third-party/libspectre/ps.h"
+
+#include <ghostscript/iapi.h>
+#include <ghostscript/ierrors.h>
+
+
+/************************************************
+
+ ************************************************/
+PsFilePage::PsFilePage(PsFile *file, const PsFilePos &pos, int pageNum):
+ mFile(file),
+ mPageNum(pageNum),
+ mPos(pos)
+{
+
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsFilePage::setRect(const QRect &value)
+{
+ mRect = value;
+}
+
+
+/************************************************
+
+ ************************************************/
+PsFile::PsFile(const QString &fileName, QObject *parent):
+ QObject(parent),
+ mFile(fileName)
+{
+ parse();
+}
+
+
+/************************************************
+
+ ************************************************/
+PsFile::~PsFile()
+{
+
+}
+
+
+/************************************************
+
+ ************************************************/
+QString PsFile::fileName() const
+{
+ return mFile.fileName();
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsFile::writeFilePart(const PsFilePos &pos, QTextStream *out)
+{
+ writeFilePart(pos.begin, pos.end, out);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsFile::writeFilePart(long begin, long end, QTextStream *out)
+{
+ if (!mFile.isOpen())
+ mFile.open(QIODevice::ReadOnly | QIODevice::Text);
+
+ mFile.seek(begin);
+ QByteArray buf = mFile.read(end - begin);
+ *out << buf;
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsFile::writePageBody(const PsFilePos &pos, QTextStream *out)
+{
+ writePageBody(pos.begin, pos.end, out);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsFile::writePageBody(long begin, long end, QTextStream *out)
+{
+ mFile.seek(begin);
+
+ // Skeep %%Page: tag line
+ QString s = mFile.readLine();
+ qint64 len = end - begin - s.length();
+
+ QByteArray buf = mFile.read(len);
+ *out << buf;
+}
+
+
+/************************************************
+
+ ************************************************/
+bool PsFile::parse()
+{
+ document *doc = psscan(mFile.fileName().toLocal8Bit(), SCANSTYLE_NORMAL);
+
+ if (!doc)
+ {
+ qWarning() << QString("Can't scan input file %1").arg(mFile.fileName());
+ return false;
+ }
+
+ mTitle = QString::fromLocal8Bit(doc->title);
+ mPrologPos.begin = doc->beginprolog;
+ mPrologPos.end = doc->endprolog;
+
+ mSetupPos.begin = doc->beginsetup;
+ mSetupPos.end = doc->endsetup;
+
+ mTrailerPos.begin = doc->begintrailer;
+ mTrailerPos.end = doc->endtrailer;
+
+ mPages.clear();
+ for (int i = 0; i < doc->numpages; i++)
+ {
+ PsFilePos pos;
+ pos.begin = doc->pages[i].begin;
+ pos.end = doc->pages[i].end;
+ PsFilePage psPage(this, pos, i);
+
+ int rx=0;
+ int ry=0;
+ int lx=0;
+ int ly=0;
+ psgetpagebox(doc, i, &rx, &ry, &lx, &ly);
+ psPage.setRect(QRect(lx, ly, rx - lx, ry - ly));
+ mPages << psPage;
+ }
+
+ psdocdestroy(doc);
+
+ return true;
+}
+
+
+/************************************************
+
+ ************************************************/
+GsMergeFile::GsMergeFile(const QString &fileName, QObject *parent):
+ PsFile(fileName, parent)
+{
+}
+
+
+/************************************************
+
+ ************************************************/
+GsMergeFile::~GsMergeFile()
+{
+ QFile::remove(fileName());
+}
+
+
+/************************************************
+
+ ************************************************/
+bool GsMergeFile::merge(const QStringList inputFiles)
+{
+ if (inputFiles.isEmpty())
+ {
+ mPages.clear();
+ mFile.close();
+ QFile::resize(mFile.fileName(), 0);
+ return true;
+ }
+
+ mFile.close();
+ void *gsInstance;
+ int gsRes;
+ gsRes = gsapi_new_instance(&gsInstance, 0);
+ if (gsRes < 0)
+ return false;
+
+
+ QList<QByteArray> args;
+ args << "merge";
+ args << "-q";
+ args << "-dNOPAUSE";
+ args << "-dBATCH";
+ args << "-dHaveTrueTypes=false";
+ args << "-sDEVICE=ps2write";
+ args << QString("-sOutputFile=%1").arg(fileName()).toLocal8Bit();
+ for (int i=0; i<inputFiles.count(); ++i)
+ args << inputFiles.at(i).toLocal8Bit();
+
+ int argc = args.count();
+ char *argv[100];
+ for (int i = 0; i < argc; ++i)
+ {
+ argv[i] = args[i].data();
+ }
+
+ // qDebug() << args;
+
+ gsRes = gsapi_init_with_args(gsInstance, argc, argv);
+ if (gsRes < -100)
+ {
+ gsapi_delete_instance(gsInstance);
+ return false;
+ }
+
+ gsRes = gsapi_exit(gsInstance);
+ gsapi_delete_instance(gsInstance);
+
+ mPages.clear();
+ if (gsRes != 0 && gsRes != e_Quit)
+ return false;
+
+ return parse();
+}
+
+
+/************************************************
+
+ ************************************************/
+bool GsMergeFile::merge(const QList<PsFile *> inputFiles)
+{
+ QStringList files;
+ foreach (PsFile *f, inputFiles)
+ files << f->fileName();
+
+ return merge(files);
+}
119 gui/kernel/psfile.h
@@ -0,0 +1,119 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#ifndef PSFILE_H
+#define PSFILE_H
+
+
+#include <QObject>
+#include <QList>
+#include <QFile>
+#include <QRect>
+
+class PsFile;
+class QTextStream;
+
+struct PsFilePos
+{
+ long begin;
+ long end;
+};
+
+
+class PsFilePage
+{
+ friend class PsFile;
+public:
+ PsFilePage(PsFile *file, const PsFilePos &pos, int pageNum);
+
+ PsFile *file() const { return mFile; }
+ int pageNum() const { return mPageNum; }
+ PsFilePos filePos() const { return mPos; }
+ QRect rect() const { return mRect; }
+
+protected:
+ void setRect(const QRect &value);
+
+private:
+ PsFile *mFile;
+ PsFilePos mPos;
+ int mPageNum;
+ QRect mRect;
+};
+
+
+class PsFile : public QObject
+{
+ Q_OBJECT
+public:
+ explicit PsFile(const QString &fileName, QObject *parent = 0);
+ virtual ~PsFile();
+
+ QString fileName() const;
+
+ QString title() const { return mTitle; }
+
+ PsFilePage page(int index) { return mPages[index]; }
+ int pageCount() const { return mPages.count(); }
+
+ PsFilePos prologPos() const { return mPrologPos; }
+ PsFilePos setupPos() const { return mSetupPos; }
+ PsFilePos trailerPos() const { return mTrailerPos; }
+
+ void writeFilePart(const PsFilePos &pos, QTextStream *out);
+ void writeFilePart(long begin, long end, QTextStream *out);
+
+ void writePageBody(const PsFilePos &pos, QTextStream *out);
+ void writePageBody(long begin, long end, QTextStream *out);
+
+protected:
+ bool parse();
+ QFile mFile;
+ QList<PsFilePage> mPages;
+
+private:
+ QString mTitle;
+ PsFilePos mPrologPos;
+ PsFilePos mSetupPos;
+ PsFilePos mTrailerPos;
+};
+
+
+class GsMergeFile: public PsFile
+{
+ Q_OBJECT
+public:
+ GsMergeFile(const QString &fileName, QObject *parent = 0);
+ virtual ~GsMergeFile();
+
+ bool merge(const QStringList inputFiles);
+ bool merge(const QList<PsFile*> inputFiles);
+
+private:
+
+};
+
+#endif // PSFILE_H
334 gui/kernel/psproject.cpp
@@ -0,0 +1,334 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+#include "psfile.h"
+#include "kernel/psproject.h"
+#include "kernel/psengine.h"
+#include "settings.h"
+
+#include <QCoreApplication>
+#include <QString>
+#include <QStringList>
+#include <QDir>
+#include <QDebug>
+
+#include <math.h>
+
+/************************************************
+
+ ************************************************/
+PsProject::PsProject(QObject *parent) :
+ QObject(parent),
+ mEngine(0),
+ mPrinter(&mNullPrinter)
+{
+ mPsFile = new GsMergeFile(QDir::tempPath() + QString("/bprint_%1.ps").arg(QCoreApplication::applicationPid()), this);
+
+ setLayout(settings->layout());
+}
+
+
+/************************************************
+
+ ************************************************/
+PsProject::~PsProject()
+{
+ settings->setLayout(mLayout);
+ settings->sync();
+ qDeleteAll(mPages);
+ qDeleteAll(mFiles);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsProject::addFile(QString fileName)
+{
+ emit fileAboutToBeAdded(fileName);
+
+ PsFile *file = new PsFile(fileName, this);
+ mFiles << file;
+
+ for (int i=0; i<file->pageCount(); ++i)
+ {
+ PsProjectPage *p = new PsProjectPage(file, i);
+ mPages << p;
+ }
+
+ mPsFile->merge(mFiles);
+ updatePages();
+ updateSheets();
+
+ emit fileAdded(file);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsProject::removeFile(int index)
+{
+ emit fileAboutToBeRemoved(mFiles.at(index));
+
+ PsFile *oldFile = mFiles.takeAt(index);
+
+ for (int i = mPages.count()-1; i>-1; --i)
+ {
+ if (mPages.at(i)->file() == oldFile)
+ {
+ delete mPages.takeAt(i);
+ }
+ }
+ delete oldFile;
+
+ mPsFile->merge(mFiles);
+ updatePages();
+ updateSheets();
+
+ emit fileRemoved();
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsProject::updatePages()
+{
+ int n=0;
+ foreach(const PsFile *f, mFiles)
+ {
+ foreach(PsProjectPage *page, mPages)
+ {
+ if (page->file() == f)
+ {
+ const PsFilePage &fPage = mPsFile->page(n + page->pageNum());
+ page->setBegin(fPage.filePos().begin);
+ page->setEnd(fPage.filePos().end);
+ page->setRect(fPage.rect());
+ }
+ }
+
+ n+=f->pageCount();
+ }
+}
+
+
+/************************************************
+
+ ************************************************/
+int PsProject::pageCount() const
+{
+ return mPages.count();
+}
+
+
+/************************************************
+
+ ************************************************/
+PsProjectPage *PsProject::page(int index)
+{
+ return mPages[index];
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsProject::updateSheets()
+{
+ qDeleteAll(mSheets);
+ mSheets.clear();
+ if (!mPages.isEmpty())
+ mEngine->fillSheets(&mSheets);
+
+ qDeleteAll(mPreviewSheets);
+ mPreviewSheets.clear();
+ if (!mPages.isEmpty())
+ mEngine->fillPreviewSheets(&mPreviewSheets);
+
+ emit changed();
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsProject::writeDocument(const QList<const PsSheet *> &sheets, QTextStream *out)
+{
+ mEngine->writeDocument(sheets, out);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsProject::writeDocument(PsProject::PagesType pages, QTextStream *out)
+{
+ writeDocument(pages, ForwardOrder, out);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsProject::writeDocument(PsProject::PagesType pages, PsProject::PagesOrder order, QTextStream *out)
+{
+ int start;
+ int inc;
+ int end = sheetCount();
+
+ switch (pages)
+ {
+ case PsProject::OddPages:
+ start = 0;
+ inc = 2;
+ break;
+
+ case PsProject::EvenPages:
+ start = 1;
+ inc = 2;
+ break;
+
+ case PsProject::AllPages:
+ start = 0;
+ inc = 1;
+ break;
+ }
+
+ QList<const PsSheet *> sheets;
+
+ if (order == PsProject::ForwardOrder)
+ {
+ for (int i=start; i < end; i += inc)
+ sheets << mSheets.at(i);
+ }
+ else
+ {
+ for (int i=end; i >=start; i -= inc)
+ sheets << mSheets.at(i);
+ }
+
+ writeDocument(sheets, out);
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsProject::setLayout(PsProject::PsLayout layout)
+{
+ if (!mEngine || mLayout != layout)
+ {
+ mLayout = layout;
+ PsEngine *oldEngine = mEngine;
+ switch (mLayout)
+ {
+ case Layout1Up:
+ mEngine = new EngineNUp(this, 1, 1);
+ break;
+
+ case Layout2Up:
+ mEngine = new EngineNUp(this, 2, 1);
+ break;
+
+ case Layout4Up:
+ mEngine = new EngineNUp(this, 2, 2);
+ break;
+
+ case Layout8Up:
+ mEngine = new EngineNUp(this, 4, 2);
+ break;
+
+ case LayoutBooklet:
+ mEngine = new EngineBooklet(this);
+ break;
+ }
+
+ delete oldEngine;
+
+ updateSheets();
+ }
+}
+
+
+/************************************************
+
+ ************************************************/
+QString PsProject::layoutToStr(PsProject::PsLayout value)
+{
+ switch (value)
+ {
+ case Layout1Up: return "1up";
+ case Layout2Up: return "2up";
+ case Layout4Up: return "4up";
+ case Layout8Up: return "8up";
+ case LayoutBooklet: return "Booklet";
+ }
+}
+
+
+/************************************************
+
+ ************************************************/
+PsProject::PsLayout PsProject::strToLayout(const QString &value)
+{
+ QString s = value.toUpper();
+ if (s == "1UP") return Layout1Up;
+ if (s == "2UP") return Layout2Up;
+ if (s == "4UP") return Layout4Up;
+ if (s == "8UP") return Layout4Up;
+ if (s == "BOOKLET") return LayoutBooklet;
+
+ return LayoutBooklet;
+}
+
+
+/************************************************
+
+ ************************************************/
+void PsProject::setPrinter(Printer *value)
+{
+ if (value)
+ mPrinter = value;
+ else
+ mPrinter = &mNullPrinter;
+
+ updateSheets();
+ emit changed();
+}
+
+
+/************************************************
+
+ ************************************************/
+PsProjectPage::PsProjectPage(PsFile *file, int pageNum):
+ mFile(file),
+ mPageNum(pageNum)
+{
+
+}
+
+
168 gui/kernel/psproject.h
@@ -0,0 +1,168 @@
+/* BEGIN_COMMON_COPYRIGHT_HEADER
+ * (c)LGPL2+
+ *
+ *
+ * Copyright: 2012-2013 Alexander Sokoloff
+ * Authors:
+ * Alexander Sokoloff <sokoloff.a@gmail.com>
+ *
+ * This program or library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * END_COMMON_COPYRIGHT_HEADER */
+
+
+#ifndef PSPROJECT_H
+#define PSPROJECT_H
+
+#include "pssheet.h"
+#include "printer.h"
+
+#include <QObject>
+#include <QList>
+
+
+class PsFile;
+class PsEngine;
+class PsProjectPage;
+
+class PsProject : public