2 Star 0 Fork 0

mirrors_anthonyfok/scantailor

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
ProjectPages.cpp 16.91 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
/*
Scan Tailor - Interactive post-processing tool for scanned pages.
Copyright (C) Joseph Artsimovich <[email protected]>
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "ProjectPages.h"
#include "ProjectPages.h.moc"
#include "ImageFileInfo.h"
#include "ImageMetadata.h"
#include "ImageInfo.h"
#include "OrthogonalRotation.h"
#include "PageSequence.h"
#include "RelinkablePath.h"
#include "AbstractRelinker.h"
#include <boost/foreach.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/member.hpp>
#include <QMutexLocker>
#include <QFileInfo>
#include <QSize>
#include <QDebug>
#include <map>
#include <algorithm>
#include <stddef.h>
#include <assert.h>
ProjectPages::ProjectPages(Qt::LayoutDirection const layout_direction)
{
initSubPagesInOrder(layout_direction);
}
ProjectPages::ProjectPages(
std::vector<ImageInfo> const& info,
Qt::LayoutDirection const layout_direction)
{
initSubPagesInOrder(layout_direction);
BOOST_FOREACH(ImageInfo const& image, info) {
ImageDesc image_desc(image);
// Enforce some rules.
if (image_desc.numLogicalPages == 2) {
image_desc.leftHalfRemoved = false;
image_desc.rightHalfRemoved = false;
} else if (image_desc.numLogicalPages != 1) {
continue;
} else if (image_desc.leftHalfRemoved && image_desc.rightHalfRemoved) {
image_desc.leftHalfRemoved = false;
image_desc.rightHalfRemoved = false;
}
m_images.push_back(image_desc);
}
}
ProjectPages::ProjectPages(
std::vector<ImageFileInfo> const& files,
Pages const pages, Qt::LayoutDirection const layout_direction)
{
initSubPagesInOrder(layout_direction);
BOOST_FOREACH(ImageFileInfo const& file, files) {
QString const& file_path = file.fileInfo().absoluteFilePath();
std::vector<ImageMetadata> const& images = file.imageInfo();
int const num_images = images.size();
int const multi_page_base = num_images > 1 ? 1 : 0;
for (int i = 0; i < num_images; ++i) {
ImageMetadata const& metadata = images[i];
ImageId const id(file_path, multi_page_base + i);
m_images.push_back(ImageDesc(id, metadata, pages));
}
}
}
ProjectPages::~ProjectPages()
{
}
Qt::LayoutDirection
ProjectPages::layoutDirection() const
{
if (m_subPagesInOrder[0] == PageId::LEFT_PAGE) {
return Qt::LeftToRight;
} else {
assert(m_subPagesInOrder[0] == PageId::RIGHT_PAGE);
return Qt::RightToLeft;
}
}
void
ProjectPages::initSubPagesInOrder(Qt::LayoutDirection const layout_direction)
{
if (layout_direction == Qt::LeftToRight) {
m_subPagesInOrder[0] = PageId::LEFT_PAGE;
m_subPagesInOrder[1] = PageId::RIGHT_PAGE;
} else {
m_subPagesInOrder[0] = PageId::RIGHT_PAGE;
m_subPagesInOrder[1] = PageId::LEFT_PAGE;
}
}
PageSequence
ProjectPages::toPageSequence(PageView const view) const
{
PageSequence pages;
if (view == PAGE_VIEW) {
QMutexLocker locker(&m_mutex);
int const num_images = m_images.size();
for (int i = 0; i < num_images; ++i) {
ImageDesc const& image = m_images[i];
assert(image.numLogicalPages >= 1 && image.numLogicalPages <= 2);
for (int j = 0; j < image.numLogicalPages; ++j) {
PageId const id(
image.id,
image.logicalPageToSubPage(
j, m_subPagesInOrder
)
);
pages.append(
PageInfo(
id, image.metadata,
image.numLogicalPages,
image.leftHalfRemoved,
image.rightHalfRemoved
)
);
}
}
} else {
assert(view == IMAGE_VIEW);
QMutexLocker locker(&m_mutex);
int const num_images = m_images.size();
for (int i = 0; i < num_images; ++i) {
ImageDesc const& image = m_images[i];
PageId const id(image.id, PageId::SINGLE_PAGE);
pages.append(
PageInfo(
id, image.metadata,
image.numLogicalPages,
image.leftHalfRemoved,
image.rightHalfRemoved
)
);
}
}
return pages;
}
void
ProjectPages::listRelinkablePaths(VirtualFunction1<void, RelinkablePath const&>& sink) const
{
// It's generally a bad idea to do callbacks while holding an internal mutex,
// so we accumulate results into this vector first.
std::vector<QString> files;
{
QMutexLocker locker(&m_mutex);
files.reserve(m_images.size());
BOOST_FOREACH(ImageDesc const& image, m_images) {
files.push_back(image.id.filePath());
}
}
BOOST_FOREACH(QString const& file, files) {
sink(RelinkablePath(file, RelinkablePath::File));
}
}
void
ProjectPages::performRelinking(AbstractRelinker const& relinker)
{
QMutexLocker locker(&m_mutex);
BOOST_FOREACH(ImageDesc& image, m_images) {
RelinkablePath const old_path(image.id.filePath(), RelinkablePath::File);
QString const new_path(relinker.substitutionPathFor(old_path));
image.id.setFilePath(new_path);
}
}
void
ProjectPages::setLayoutTypeFor(ImageId const& image_id, LayoutType const layout)
{
bool was_modified = false;
{
QMutexLocker locker(&m_mutex);
setLayoutTypeForImpl(image_id, layout, &was_modified);
}
if (was_modified) {
emit modified();
}
}
void
ProjectPages::setLayoutTypeForAllPages(LayoutType const layout)
{
bool was_modified = false;
{
QMutexLocker locker(&m_mutex);
setLayoutTypeForAllPagesImpl(layout, &was_modified);
}
if (was_modified) {
emit modified();
}
}
void
ProjectPages::autoSetLayoutTypeFor(
ImageId const& image_id, OrthogonalRotation const rotation)
{
bool was_modified = false;
{
QMutexLocker locker(&m_mutex);
autoSetLayoutTypeForImpl(image_id, rotation, &was_modified);
}
if (was_modified) {
emit modified();
}
}
void
ProjectPages::updateImageMetadata(
ImageId const& image_id, ImageMetadata const& metadata)
{
bool was_modified = false;
{
QMutexLocker locker(&m_mutex);
updateImageMetadataImpl(image_id, metadata, &was_modified);
}
if (was_modified) {
emit modified();
}
}
int
ProjectPages::adviseNumberOfLogicalPages(
ImageMetadata const& metadata, OrthogonalRotation const rotation)
{
QSize const size(rotation.rotate(metadata.size()));
QSize const dpi(rotation.rotate(metadata.dpi().toSize()));
if (size.width() * dpi.height() > size.height() * dpi.width()) {
return 2;
} else {
return 1;
}
}
int
ProjectPages::numImages() const
{
QMutexLocker locker(&m_mutex);
return m_images.size();
}
std::vector<PageInfo>
ProjectPages::insertImage(
ImageInfo const& new_image, BeforeOrAfter before_or_after,
ImageId const& existing, PageView const view)
{
bool was_modified = false;
{
QMutexLocker locker(&m_mutex);
return insertImageImpl(
new_image, before_or_after, existing, view, was_modified
);
}
if (was_modified) {
emit modified();
}
}
void
ProjectPages::removePages(std::set<PageId> const& pages)
{
bool was_modified = false;
{
QMutexLocker locker(&m_mutex);
removePagesImpl(pages, was_modified);
}
if (was_modified) {
emit modified();
}
}
PageInfo
ProjectPages::unremovePage(PageId const& page_id)
{
bool was_modified = false;
PageInfo page_info;
{
QMutexLocker locker(&m_mutex);
page_info = unremovePageImpl(page_id, was_modified);
}
if (was_modified) {
emit modified();
}
return page_info;
}
bool
ProjectPages::validateDpis() const
{
QMutexLocker locker(&m_mutex);
BOOST_FOREACH(ImageDesc const& image, m_images) {
if (!image.metadata.isDpiOK()) {
return false;
}
}
return true;
}
namespace
{
struct File
{
QString fileName;
mutable std::vector<ImageMetadata> metadata;
File(QString const& fname) : fileName(fname) {}
operator ImageFileInfo() const { return ImageFileInfo(fileName, metadata); }
};
} // anonymous namespace
std::vector<ImageFileInfo>
ProjectPages::toImageFileInfo() const
{
using namespace boost::multi_index;
multi_index_container<
File,
indexed_by<
ordered_unique<member<File, QString, &File::fileName> >,
sequenced<>
>
> files;
{
QMutexLocker locker(&m_mutex);
BOOST_FOREACH(ImageDesc const& image, m_images) {
File const file(image.id.filePath());
files.insert(file).first->metadata.push_back(image.metadata);
}
}
return std::vector<ImageFileInfo>(files.get<1>().begin(), files.get<1>().end());
}
void
ProjectPages::updateMetadataFrom(std::vector<ImageFileInfo> const& files)
{
typedef std::map<ImageId, ImageMetadata> MetadataMap;
MetadataMap metadata_map;
BOOST_FOREACH(ImageFileInfo const& file, files) {
QString const file_path(file.fileInfo().absoluteFilePath());
int page = 0;
BOOST_FOREACH(ImageMetadata const& metadata, file.imageInfo()) {
metadata_map[ImageId(file_path, page)] = metadata;
++page;
}
}
QMutexLocker locker(&m_mutex);
BOOST_FOREACH(ImageDesc& image, m_images) {
MetadataMap::const_iterator const it(metadata_map.find(image.id));
if (it != metadata_map.end()) {
image.metadata = it->second;
}
}
}
void
ProjectPages::setLayoutTypeForImpl(
ImageId const& image_id, LayoutType const layout, bool* modified)
{
int const num_pages = (layout == TWO_PAGE_LAYOUT ? 2 : 1);
int const num_images = m_images.size();
for (int i = 0; i < num_images; ++i) {
ImageDesc& image = m_images[i];
if (image.id == image_id) {
int adjusted_num_pages = num_pages;
if (num_pages == 2 && image.leftHalfRemoved != image.rightHalfRemoved) {
// Both can't be removed, but we handle that case anyway
// by treating it like none are removed.
--adjusted_num_pages;
}
int const delta = adjusted_num_pages - image.numLogicalPages;
if (delta == 0) {
break;
}
image.numLogicalPages = adjusted_num_pages;
*modified = true;
break;
}
}
}
void
ProjectPages::setLayoutTypeForAllPagesImpl(
LayoutType const layout, bool* modified)
{
int const num_pages = (layout == TWO_PAGE_LAYOUT ? 2 : 1);
int const num_images = m_images.size();
for (int i = 0; i < num_images; ++i) {
ImageDesc& image = m_images[i];
int adjusted_num_pages = num_pages;
if (num_pages == 2 && image.leftHalfRemoved != image.rightHalfRemoved) {
// Both can't be removed, but we handle that case anyway
// by treating it like none are removed.
--adjusted_num_pages;
}
int const delta = adjusted_num_pages - image.numLogicalPages;
if (delta == 0) {
continue;
}
image.numLogicalPages = adjusted_num_pages;
*modified = true;
}
}
void
ProjectPages::autoSetLayoutTypeForImpl(
ImageId const& image_id, OrthogonalRotation const rotation, bool* modified)
{
int const num_images = m_images.size();
for (int i = 0; i < num_images; ++i) {
ImageDesc& image = m_images[i];
if (image.id == image_id) {
int num_pages = adviseNumberOfLogicalPages(image.metadata, rotation);
if (num_pages == 2 && image.leftHalfRemoved != image.rightHalfRemoved) {
// Both can't be removed, but we handle that case anyway
// by treating it like none are removed.
--num_pages;
}
int const delta = num_pages - image.numLogicalPages;
if (delta == 0) {
break;
}
image.numLogicalPages = num_pages;
*modified = true;
break;
}
}
}
void
ProjectPages::updateImageMetadataImpl(
ImageId const& image_id,
ImageMetadata const& metadata, bool* modified)
{
int const num_images = m_images.size();
for (int i = 0; i < num_images; ++i) {
ImageDesc& image = m_images[i];
if (image.id == image_id) {
if (image.metadata != metadata) {
image.metadata = metadata;
*modified = true;
}
break;
}
}
}
std::vector<PageInfo>
ProjectPages::insertImageImpl(
ImageInfo const& new_image, BeforeOrAfter before_or_after,
ImageId const& existing, PageView const view, bool& modified)
{
std::vector<PageInfo> logical_pages;
std::vector<ImageDesc>::iterator it(m_images.begin());
std::vector<ImageDesc>::iterator const end(m_images.end());
for (; it != end && it->id != existing; ++it) {
// Continue until we find the existing image.
}
if (it == end) {
// Existing image not found.
if (!(before_or_after == BEFORE && existing.isNull())) {
return logical_pages;
} // Otherwise we can still handle that case.
}
if (before_or_after == AFTER) {
++it;
}
ImageDesc image_desc(new_image);
// Enforce some rules.
if (image_desc.numLogicalPages == 2) {
image_desc.leftHalfRemoved = false;
image_desc.rightHalfRemoved = false;
} else if (image_desc.numLogicalPages != 1) {
return logical_pages;
} else if (image_desc.leftHalfRemoved && image_desc.rightHalfRemoved) {
image_desc.leftHalfRemoved = false;
image_desc.rightHalfRemoved = false;
}
m_images.insert(it, image_desc);
PageInfo page_info_templ(
PageId(new_image.id(), PageId::SINGLE_PAGE),
image_desc.metadata, image_desc.numLogicalPages,
image_desc.leftHalfRemoved, image_desc.rightHalfRemoved
);
if (view == IMAGE_VIEW || (image_desc.numLogicalPages == 1 &&
image_desc.leftHalfRemoved == image_desc.rightHalfRemoved)) {
logical_pages.push_back(page_info_templ);
} else {
if (image_desc.numLogicalPages == 2 ||
(image_desc.numLogicalPages == 1 && image_desc.rightHalfRemoved)) {
page_info_templ.setId(PageId(new_image.id(), m_subPagesInOrder[0]));
logical_pages.push_back(page_info_templ);
}
if (image_desc.numLogicalPages == 2 ||
(image_desc.numLogicalPages == 1 && image_desc.leftHalfRemoved)) {
page_info_templ.setId(PageId(new_image.id(), m_subPagesInOrder[1]));
logical_pages.push_back(page_info_templ);
}
}
return logical_pages;
}
void
ProjectPages::removePagesImpl(std::set<PageId> const& to_remove, bool& modified)
{
std::set<PageId>::const_iterator const to_remove_end(to_remove.end());
std::vector<ImageDesc> new_images;
new_images.reserve(m_images.size());
int new_total_logical_pages = 0;
int const num_old_images = m_images.size();
for (int i = 0; i < num_old_images; ++i) {
ImageDesc image(m_images[i]);
if (to_remove.find(PageId(image.id, PageId::SINGLE_PAGE)) != to_remove_end) {
image.numLogicalPages = 0;
modified = true;
} else {
if (to_remove.find(PageId(image.id, PageId::LEFT_PAGE)) != to_remove_end) {
image.leftHalfRemoved = true;
--image.numLogicalPages;
modified = true;
}
if (to_remove.find(PageId(image.id, PageId::RIGHT_PAGE)) != to_remove_end) {
image.rightHalfRemoved = true;
--image.numLogicalPages;
modified = true;
}
}
if (image.numLogicalPages > 0) {
new_images.push_back(image);
new_total_logical_pages += new_images.back().numLogicalPages;
}
}
new_images.swap(m_images);
}
PageInfo
ProjectPages::unremovePageImpl(PageId const& page_id, bool& modified)
{
if (page_id.subPage() == PageId::SINGLE_PAGE) {
// These can't be unremoved.
return PageInfo();
}
std::vector<ImageDesc>::iterator it(m_images.begin());
std::vector<ImageDesc>::iterator const end(m_images.end());
for (; it != end && it->id != page_id.imageId(); ++it) {
// Continue until we find the corresponding image.
}
if (it == end) {
// The corresponding image wasn't found.
return PageInfo();
}
ImageDesc& image = *it;
if (image.numLogicalPages != 1) {
return PageInfo();
}
if (page_id.subPage() == PageId::LEFT_PAGE && image.leftHalfRemoved) {
image.leftHalfRemoved = false;
} else if (page_id.subPage() == PageId::RIGHT_PAGE && image.rightHalfRemoved) {
image.rightHalfRemoved = false;
} else {
return PageInfo();
}
image.numLogicalPages = 2;
return PageInfo(
page_id, image.metadata, image.numLogicalPages,
image.leftHalfRemoved, image.rightHalfRemoved
);
}
/*========================= ProjectPages::ImageDesc ======================*/
ProjectPages::ImageDesc::ImageDesc(ImageInfo const& image_info)
: id(image_info.id()),
metadata(image_info.metadata()),
numLogicalPages(image_info.numSubPages()),
leftHalfRemoved(image_info.leftHalfRemoved()),
rightHalfRemoved(image_info.rightHalfRemoved())
{
}
ProjectPages::ImageDesc::ImageDesc(
ImageId const& id, ImageMetadata const& metadata, Pages const pages)
: id(id),
metadata(metadata),
leftHalfRemoved(false),
rightHalfRemoved(false)
{
switch (pages) {
case ONE_PAGE:
numLogicalPages = 1;
break;
case TWO_PAGES:
numLogicalPages = 2;
break;
case AUTO_PAGES:
numLogicalPages = adviseNumberOfLogicalPages(
metadata, OrthogonalRotation()
);
break;
}
}
PageId::SubPage
ProjectPages::ImageDesc::logicalPageToSubPage(
int const logical_page, PageId::SubPage const* sub_pages_in_order) const
{
assert(numLogicalPages >= 1 && numLogicalPages <= 2);
assert(logical_page >= 0 && logical_page < numLogicalPages);
if (numLogicalPages == 1) {
if (leftHalfRemoved && !rightHalfRemoved) {
return PageId::RIGHT_PAGE;
} else if (rightHalfRemoved && !leftHalfRemoved) {
return PageId::LEFT_PAGE;
} else {
return PageId::SINGLE_PAGE;
}
} else {
return sub_pages_in_order[logical_page];
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors_anthonyfok/scantailor.git
[email protected]:mirrors_anthonyfok/scantailor.git
mirrors_anthonyfok
scantailor
scantailor
master

搜索帮助