X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FEmbeddedFiles.cpp;h=59e93710155d87351e7ae9a1c4dc1aefad3bde63;hb=0362c6aae73c293d1c20277c12d362acfe0b2ef6;hp=dac3796b17b9f40ad43d2088ce070e04d097b5db;hpb=56a1dfe65fc493f761f0d562c9e79ba1e7593fff;p=lyx.git diff --git a/src/EmbeddedFiles.cpp b/src/EmbeddedFiles.cpp index dac3796b17..59e9371015 100644 --- a/src/EmbeddedFiles.cpp +++ b/src/EmbeddedFiles.cpp @@ -1,6 +1,6 @@ // -*- C++ -*- /** - * \file EmbeddedFiles.cpp + * \file EmbeddedFileList.cpp * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * @@ -43,34 +43,11 @@ using namespace lyx::support; namespace lyx { -/** Dir name used for ".." in the bundled file. - -Under the lyx temp directory, content.lyx and its embedded files are usually -saved as - -$temp/file.lyx -$temp/figure1.png for ./figure1.png) -$temp/sub/figure2.png for ./sub/figure2.png) - -This works fine for embedded files that are in the current or deeper directory -of the document directory, but not for files such as ../figures/figure.png. -A unique name $upDirName is chosen to represent .. in such filenames so that -'up' directories can be stored 'down' the directory tree: - -$temp/$upDirName/figures/figure.png for ../figures/figure.png -$temp/$upDirName/$upDirName/figure.png for ../../figure.png - -This name has to be fixed because it is used in lyx bundled .zip file. - -Note that absolute files are not embeddable because there is no easy -way to put them under $temp. -*/ -const std::string upDirName = "LyX.Embed.Dir.Up"; - namespace Alert = frontend::Alert; EmbeddedFile::EmbeddedFile(string const & file, std::string const & buffer_path) - : DocFileName("", false), inzip_name_(""), embedded_(false), inset_list_() + : DocFileName("", false), inzip_name_(""), embedded_(false), inset_list_(), + temp_path_("") { set(file, buffer_path); } @@ -82,32 +59,41 @@ void EmbeddedFile::set(std::string const & filename, std::string const & buffer_ if (filename.empty()) return; - inzip_name_ = to_utf8(makeRelPath(from_utf8(absFilename()), - from_utf8(buffer_path))); - // if inzip_name_ is an absolute path, this file is not embeddable - if (FileName(inzip_name_).isAbsolute()) - inzip_name_ = ""; - // replace .. by upDirName - if (prefixIs(inzip_name_, ".")) - inzip_name_ = subst(inzip_name_, "..", upDirName); - LYXERR(Debug::FILES, "Create embedded file " << filename << - " with in zip name " << inzip_name_ << endl); + inzip_name_ = calcInzipName(buffer_path); +} + + +void EmbeddedFile::setInzipName(std::string const & name) +{ + if (name.empty() || name == inzip_name_) + return; + + // an enabled EmbeededFile should have this problem handled + BOOST_ASSERT(!enabled()); + // file will be synced when it is enabled + inzip_name_ = name; } -string EmbeddedFile::embeddedFile(Buffer const * buf) const +string EmbeddedFile::embeddedFile() const { - BOOST_ASSERT(embeddable()); - string temp = buf->temppath(); - if (!suffixIs(temp, '/')) - temp += '/'; - return temp + inzip_name_; + BOOST_ASSERT(enabled()); + return temp_path_ + inzip_name_; } -string EmbeddedFile::availableFile(Buffer const * buf) const +FileName EmbeddedFile::availableFile() const { - return embedded() ? embeddedFile(buf) : absFilename(); + if (enabled() && embedded()) + return FileName(embeddedFile()); + else + return *this; +} + + +string EmbeddedFile::latexFilename(std::string const & buffer_path) const +{ + return (enabled() && embedded()) ? inzip_name_ : relFilename(buffer_path); } @@ -118,41 +104,53 @@ void EmbeddedFile::addInset(Inset const * inset) } -Inset const * EmbeddedFile::inset(int idx) const +void EmbeddedFile::setEmbed(bool embed) { - BOOST_ASSERT(idx < refCount()); - // some embedded file do not have a valid par iterator - return inset_list_[idx]; + embedded_ = embed; } -void EmbeddedFile::setEmbed(bool embed) +void EmbeddedFile::enable(bool flag, Buffer const * buf) { - if (!embeddable() && embed) { - Alert::error(_("Embedding failed."), bformat( - _("Cannot embed file %1$s because its path is not relative to document path."), - from_utf8(absFilename()))); + if (enabled() == flag) return; + + if (flag) { + temp_path_ = buf->temppath(); + if (!suffixIs(temp_path_, '/')) + temp_path_ += '/'; + if (embedded()) { + if (inzip_name_ != calcInzipName(buf->filePath())) + syncInzipFile(buf->filePath()); + updateFromExternalFile(); + } else + extract(); + } else { + extract(); + temp_path_ = ""; } - embedded_ = embed; } -bool EmbeddedFile::extract(Buffer const * buf) const +bool EmbeddedFile::extract() const { - BOOST_ASSERT(embeddable()); + BOOST_ASSERT(enabled()); string ext_file = absFilename(); - string emb_file = embeddedFile(buf); + string emb_file = embeddedFile(); FileName emb(emb_file); FileName ext(ext_file); - if (!emb.exists()) - throw ExceptionMessage(ErrorException, _("Failed to extract file"), - bformat(_("Cannot extract file '%1$s'.\n" - "Source file %2$s does not exist"), - from_utf8(outputFilename()), from_utf8(emb_file))); + if (!emb.exists()) { + if (ext.exists()) + return true; + else + throw ExceptionMessage(ErrorException, _("Failed to extract file"), + bformat(_("Cannot extract file '%1$s'.\n" + "Source file %2$s does not exist"), + from_utf8(outputFilename()), from_utf8(emb_file))); + } // if external file already exists ... if (ext.exists()) { @@ -194,12 +192,12 @@ bool EmbeddedFile::extract(Buffer const * buf) const } -bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const +bool EmbeddedFile::updateFromExternalFile() const { - BOOST_ASSERT(embeddable()); + BOOST_ASSERT(enabled()); string ext_file = absFilename(); - string emb_file = embeddedFile(buf); + string emb_file = embeddedFile(); FileName emb(emb_file); FileName ext(ext_file); @@ -257,6 +255,107 @@ void EmbeddedFile::updateInsets(Buffer const * buf) const } +bool EmbeddedFile::isReadableFile() const +{ + return availableFile().isReadableFile(); +} + + +unsigned long EmbeddedFile::checksum() const +{ + return availableFile().checksum(); +} + +/** +Under the lyx temp directory, content.lyx and its embedded files are usually +saved as + +$temp/$embDirName/file.lyx +$temp/$embDirName/figure1.png for ./figure1.png) +$temp/$embDirName/sub/figure2.png for ./sub/figure2.png) + +This works fine for embedded files that are in the current or deeper directory +of the document directory, but not for files such as ../figures/figure.png. +A unique name $upDirName is chosen to represent .. in such filenames so that +'up' directories can be stored 'down' the directory tree: + +$temp/$embDirName/$upDirName/figures/figure.png for ../figures/figure.png +$temp/$embDirName/$upDirName/$upDirName/figure.png for ../../figure.png + +This name has to be fixed because it is used in lyx bundled .zip file. + +Using a similar trick, we use $absDirName for absolute path so that +an absolute filename can be saved as + +$temp/$embDirName/$absDirName/a/absolute/path for /a/absolute/path + +*/ +const std::string embDirName = "LyX.Embedded.Files"; +const std::string upDirName = "LyX.Embed.Dir.Up"; +const std::string absDirName = "LyX.Embed.Dir.Abs"; +const std::string driveName = "LyX.Embed.Drive"; +const std::string spaceName = "LyX.Embed.Space"; + +std::string EmbeddedFile::calcInzipName(std::string const & buffer_path) +{ + string inzipName = to_utf8(makeRelPath(from_utf8(absFilename()), + from_utf8(buffer_path))); + + if (FileName(inzipName).isAbsolute()) + inzipName = absDirName + '/' + inzipName; + + // replace .. by upDirName + if (prefixIs(inzipName, ".")) + inzipName = subst(inzipName, "..", upDirName); + // replace special characters by their value + inzipName = subst(inzipName, ":", driveName); + inzipName = subst(inzipName, " ", spaceName); + + // to avoid name conflict between $docu_path/file and $temp_path/file + // embedded files are in a subdirectory of $temp_path. + inzipName = embDirName + '/' + inzipName; + return inzipName; +} + + +void EmbeddedFile::syncInzipFile(std::string const & buffer_path) +{ + BOOST_ASSERT(enabled()); + string old_emb_file = temp_path_ + '/' + inzip_name_; + FileName old_emb(old_emb_file); + + LYXERR(Debug::FILES, " OLD ZIP " << old_emb_file << + " NEW ZIP " << calcInzipName(buffer_path)); + + //BOOST_ASSERT(old_emb.exists()); + + string new_inzip_name = calcInzipName(buffer_path); + string new_emb_file = temp_path_ + '/' + new_inzip_name; + FileName new_emb(new_emb_file); + + // need to make directory? + FileName path = new_emb.onlyPath(); + if (!path.createPath()) { + throw ExceptionMessage(ErrorException, _("Sync file failure"), + bformat(_("Cannot create file path '%1$s'.\n" + "Please check whether the path is writeable."), + from_utf8(path.absFilename()))); + return; + } + + if (old_emb.copyTo(new_emb)) { + LYXERR(Debug::FILES, "Sync inzip file from " << inzip_name_ + << " to " << new_inzip_name); + inzip_name_ = new_inzip_name; + return; + } + throw ExceptionMessage(ErrorException, _("Sync file failure"), + bformat(_("Cannot copy file %1$s to %2$s.\n" + "Please check whether the directory exists and is writeable."), + from_utf8(old_emb_file), from_utf8(new_emb_file))); +} + + bool operator==(EmbeddedFile const & lhs, EmbeddedFile const & rhs) { return lhs.absFilename() == rhs.absFilename() @@ -271,38 +370,56 @@ bool operator!=(EmbeddedFile const & lhs, EmbeddedFile const & rhs) } -bool EmbeddedFiles::enabled() const -{ - return buffer_->params().embedded; -} - - -void EmbeddedFiles::enable(bool flag) +void EmbeddedFileList::enable(bool flag, Buffer & buffer) { - if (enabled() != flag) { - // update embedded file list - update(); - // An exception may be thrown. - if (flag) - // if enable, copy all files to temppath() - updateFromExternalFile(); + if (buffer.embedded() == flag) + return; + + // update embedded file list + update(buffer); + + int count_embedded = 0; + int count_external = 0; + std::vector::iterator it = begin(); + std::vector::iterator it_end = end(); + // an exception may be thrown + for (; it != it_end; ++it) { + it->enable(flag, &buffer); + if (it->embedded()) + count_embedded ++; else - // if disable, extract all files - extractAll(); - // if operation is successful (no exception is thrown) - buffer_->markDirty(); - buffer_->params().embedded = flag; - if (flag) - updateInsets(); + count_external ++; + } + // if operation is successful (no exception is thrown) + buffer.markDirty(); + buffer.params().embedded = flag; + + // if the operation is successful, update insets + for (it = begin(); it != it_end; ++it) + it->updateInsets(&buffer); + + // show result + if (flag) { + docstring const msg = bformat(_("%1$d external files are ignored.\n" + "%2$d embeddable files are embedded.\n"), count_external, count_embedded); + Alert::information(_("Packing all files"), msg); + } else { + docstring const msg = bformat(_("%1$d external files are ignored.\n" + "%2$d embedded files are extracted.\n"), count_external, count_embedded); + Alert::information(_("Unpacking all files"), msg); } } -EmbeddedFile & EmbeddedFiles::registerFile(EmbeddedFile const & file, Inset const * inset) +void EmbeddedFileList::registerFile(EmbeddedFile const & file, + Inset const * inset, Buffer const & buffer) { + BOOST_ASSERT(!buffer.embedded() || file.availableFile().exists()); + BOOST_ASSERT(!buffer.embedded() || file.enabled()); + // try to find this file from the list - EmbeddedFileList::iterator it = file_list_.begin(); - EmbeddedFileList::iterator it_end = file_list_.end(); + std::vector::iterator it = begin(); + std::vector::iterator it_end = end(); for (; it != it_end; ++it) if (it->absFilename() == file.absFilename()) { if (it->embedded() != file.embedded()) { @@ -311,41 +428,43 @@ EmbeddedFile & EmbeddedFiles::registerFile(EmbeddedFile const & file, Inset cons "but with different embedding status. Assuming embedding status."), from_utf8(it->outputFilename()))); it->setEmbed(true); + // update the inset with this embedding status. + const_cast(inset)->updateEmbeddedFile(buffer, *it); } it->addInset(inset); - return *it; + return; } // - file_list_.push_back(file); - file_list_.back().addInset(inset); - return file_list_.back(); + push_back(file); + back().addInset(inset); } -void EmbeddedFiles::update() +void EmbeddedFileList::update(Buffer const & buffer) { - file_list_.clear(); + clear(); - for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it) - it->registerEmbeddedFiles(*buffer_, *this); + for (InsetIterator it = inset_iterator_begin(buffer.inset()); it; ++it) + it->registerEmbeddedFiles(buffer, *this); } -bool EmbeddedFiles::writeFile(DocFileName const & filename) +bool EmbeddedFileList::writeFile(DocFileName const & filename, Buffer const & buffer) { // file in the temporary path has the content - string const content = FileName(addName(buffer_->temppath(), + string const content = FileName(addName(buffer.temppath(), "content.lyx")).toFilesystemEncoding(); vector > filenames; // add content.lyx to filenames filenames.push_back(make_pair(content, "content.lyx")); // prepare list of embedded file - EmbeddedFileList::iterator it = file_list_.begin(); - EmbeddedFileList::iterator it_end = file_list_.end(); + update(buffer); + std::vector::iterator it = begin(); + std::vector::iterator it_end = end(); for (; it != it_end; ++it) { if (it->embedded()) { - string file = it->embeddedFile(buffer_); + string file = it->embeddedFile(); if (!FileName(file).exists()) throw ExceptionMessage(ErrorException, _("Failed to write file"), bformat(_("Embedded file %1$s does not exist. Did you tamper lyx temporary directory?"), @@ -357,7 +476,7 @@ bool EmbeddedFiles::writeFile(DocFileName const & filename) } // write a zip file with all these files. Write to a temp file first, to // avoid messing up the original file in case something goes terribly wrong. - DocFileName zipfile(addName(buffer_->temppath(), + DocFileName zipfile(addName(buffer.temppath(), onlyFilename(changeExtension( filename.toFilesystemEncoding(), ".zip")))); @@ -373,73 +492,4 @@ bool EmbeddedFiles::writeFile(DocFileName const & filename) return true; } - -EmbeddedFiles::EmbeddedFileList::const_iterator -EmbeddedFiles::find(string filename) const -{ - EmbeddedFileList::const_iterator it = file_list_.begin(); - EmbeddedFileList::const_iterator it_end = file_list_.end(); - for (; it != it_end; ++it) - if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename) - return it; - return file_list_.end(); -} - - -bool EmbeddedFiles::extractAll() const -{ - EmbeddedFileList::const_iterator it = file_list_.begin(); - EmbeddedFileList::const_iterator it_end = file_list_.end(); - int count_extracted = 0; - int count_external = 0; - for (; it != it_end; ++it) - if (it->embedded()) { - if(!it->extract(buffer_)) { - throw ExceptionMessage(ErrorException, - _("Failed to extract file"), - bformat(_("Error: can not extract file %1$s.\n"), it->displayName())); - } else - count_extracted += 1; - } else - count_external += 1; - docstring const msg = bformat(_("%1$d external or non-embeddable files are ignored.\n" - "%2$d embedded files are extracted.\n"), count_external, count_extracted); - Alert::information(_("Unpacking all files"), msg); - return true; -} - - -bool EmbeddedFiles::updateFromExternalFile() const -{ - EmbeddedFileList::const_iterator it = file_list_.begin(); - EmbeddedFileList::const_iterator it_end = file_list_.end(); - int count_embedded = 0; - int count_external = 0; - for (; it != it_end; ++it) - if (it->embedded()) { - if (!it->updateFromExternalFile(buffer_)) { - throw ExceptionMessage(ErrorException, - _("Failed to embed file"), - bformat(_("Error: can not embed file %1$s.\n"), it->displayName())); - return false; - } else - count_embedded += 1; - } else - count_external += 1; - docstring const msg = bformat(_("%1$d external or non-embeddable files are ignored.\n" - "%2$d embeddable files are embedded.\n"), count_external, count_embedded); - Alert::information(_("Packing all files"), msg); - return true; -} - - -void EmbeddedFiles::updateInsets() const -{ - EmbeddedFiles::EmbeddedFileList::const_iterator it = begin(); - EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end(); - for (; it != it_end; ++it) - if (it->refCount() > 0) - it->updateInsets(buffer_); -} - }