X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FFormat.cpp;h=485715b6e3b23911d622c31897856467f748bd0e;hb=26910d5ec49395d1372dd5b9259f1bf6ed23de0a;hp=4e2f07ba487c3bfa51de47ea34fca6f3cdeb1e4f;hpb=26e5f1a8ec3eb2952e49c87c91395fdcdceddfe9;p=lyx.git diff --git a/src/Format.cpp b/src/Format.cpp index 4e2f07ba48..485715b6e3 100644 --- a/src/Format.cpp +++ b/src/Format.cpp @@ -23,7 +23,7 @@ #include "support/gettext.h" #include "support/lstrings.h" #include "support/os.h" -#include "support/Path.h" +#include "support/PathChanger.h" #include "support/Systemcall.h" #include "support/textutils.h" #include "support/Translator.h" @@ -59,7 +59,8 @@ string const token_socket_format("$$a"); class FormatNamesEqual : public unary_function { public: FormatNamesEqual(string const & name) - : name_(name) {} + : name_(name) + {} bool operator()(Format const & f) const { return f.name() == name_; @@ -72,7 +73,8 @@ private: class FormatExtensionsEqual : public unary_function { public: FormatExtensionsEqual(string const & extension) - : extension_(extension) {} + : extension_(extension) + {} bool operator()(Format const & f) const { return f.hasExtension(extension_); @@ -85,7 +87,8 @@ private: class FormatMimeEqual : public unary_function { public: FormatMimeEqual(string const & mime) - : mime_(mime) {} + : mime_(mime) + {} bool operator()(Format const & f) const { // The test for empty mime strings is needed since we allow @@ -100,7 +103,8 @@ private: class FormatPrettyNameEqual : public unary_function { public: FormatPrettyNameEqual(string const & prettyname) - : prettyname_(prettyname) {} + : prettyname_(prettyname) + {} bool operator()(Format const & f) const { return f.prettyname() == prettyname_; @@ -111,6 +115,10 @@ private: } //namespace anon +bool Format::formatSorter(Format const * lhs, Format const * rhs) +{ + return _(lhs->prettyname()) < _(rhs->prettyname()); +} bool operator<(Format const & a, Format const & b) { @@ -182,42 +190,254 @@ Format const * Formats::getFormat(string const & name) const } +namespace { + +/** Guess the file format name (as in Format::name()) from contents. + * Normally you don't want to use this directly, but rather + * Formats::getFormatFromFile(). + */ +string guessFormatFromContents(FileName const & fn) +{ + // the different filetypes and what they contain in one of the first lines + // (dots are any characters). (Herbert 20020131) + // AGR Grace... + // BMP BM... + // EPS %!PS-Adobe-3.0 EPSF... + // FIG #FIG... + // FITS ...BITPIX... + // GIF GIF... + // JPG JFIF + // PDF %PDF-... + // PNG .PNG... + // PBM P1... or P4 (B/W) + // PGM P2... or P5 (Grayscale) + // PPM P3... or P6 (color) + // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"! + // SGI \001\332... (decimal 474) + // TGIF %TGIF... + // TIFF II... or MM... + // XBM ..._bits[]... + // XPM /* XPM */ sometimes missing (f.ex. tgif-export) + // ...static char *... + // XWD \000\000\000\151 (0x00006900) decimal 105 + // + // GZIP \037\213 http://www.ietf.org/rfc/rfc1952.txt + // ZIP PK... http://www.halyava.ru/document/ind_arch.htm + // Z \037\235 UNIX compress + + // paranoia check + if (fn.empty() || !fn.isReadableFile()) + return string(); + + ifstream ifs(fn.toFilesystemEncoding().c_str()); + if (!ifs) + // Couldn't open file... + return string(); + + // gnuzip + static string const gzipStamp = "\037\213"; + + // PKZIP + static string const zipStamp = "PK"; + + // ZIP containers (koffice, openoffice.org etc). + static string const nonzipStamp = "\008\0\0\0mimetypeapplication/"; + + // compress + static string const compressStamp = "\037\235"; + + // Maximum strings to read + int const max_count = 50; + int count = 0; + + string str; + string format; + bool firstLine = true; + bool backslash = false; + int dollars = 0; + while ((count++ < max_count) && format.empty()) { + if (ifs.eof()) + break; + + getline(ifs, str); + string const stamp = str.substr(0, 2); + if (firstLine && str.size() >= 2) { + // at first we check for a zipped file, because this + // information is saved in the first bytes of the file! + // also some graphic formats which save the information + // in the first line, too. + if (prefixIs(str, gzipStamp)) { + format = "gzip"; + + } else if (stamp == zipStamp && + !contains(str, nonzipStamp)) { + format = "zip"; + + } else if (stamp == compressStamp) { + format = "compress"; + + // the graphics part + } else if (stamp == "BM") { + format = "bmp"; + + } else if (stamp == "\001\332") { + format = "sgi"; + + // PBM family + // Don't need to use str.at(0), str.at(1) because + // we already know that str.size() >= 2 + } else if (str[0] == 'P') { + switch (str[1]) { + case '1': + case '4': + format = "pbm"; + break; + case '2': + case '5': + format = "pgm"; + break; + case '3': + case '6': + format = "ppm"; + } + break; + + } else if ((stamp == "II") || (stamp == "MM")) { + format = "tiff"; + + } else if (prefixIs(str,"%TGIF")) { + format = "tgif"; + + } else if (prefixIs(str,"#FIG")) { + format = "fig"; + + } else if (prefixIs(str,"GIF")) { + format = "gif"; + + } else if (str.size() > 3) { + int const c = ((str[0] << 24) & (str[1] << 16) & + (str[2] << 8) & str[3]); + if (c == 105) { + format = "xwd"; + } + } + + firstLine = false; + } + + if (!format.empty()) + break; + else if (contains(str,"EPSF")) + // dummy, if we have wrong file description like + // %!PS-Adobe-2.0EPSF" + format = "eps"; + + else if (contains(str, "Grace")) + format = "agr"; + + else if (contains(str, "JFIF")) + format = "jpg"; + + else if (contains(str, "%PDF")) + // autodetect pdf format for graphics inclusion + format = "pdf6"; + + else if (contains(str, "PNG")) + format = "png"; + + else if (contains(str, "%!PS-Adobe")) { + // eps or ps + ifs >> str; + if (contains(str,"EPSF")) + format = "eps"; + else + format = "ps"; + } + + else if (contains(str, "_bits[]")) + format = "xbm"; + + else if (contains(str, "XPM") || contains(str, "static char *")) + format = "xpm"; + + else if (contains(str, "BITPIX")) + format = "fits"; + + else if (contains(str, "\\documentclass") || + contains(str, "\\chapter") || + contains(str, "\\section") || + contains(str, "\\begin") || + contains(str, "\\end") || + contains(str, "$$") || + contains(str, "\\[") || + contains(str, "\\]")) + format = "latex"; + else { + if (contains(str, '\\')) + backslash = true; + dollars += count_char(str, '$'); + } + } + + if (format.empty() && backslash && dollars > 1) + // inline equation + format = "latex"; + + if (format.empty()) { + if (ifs.eof()) + LYXERR(Debug::GRAPHICS, "filetools(getFormatFromContents)\n" + "\tFile type not recognised before EOF!"); + } else { + LYXERR(Debug::GRAPHICS, "Recognised Fileformat: " << format); + return format; + } + + LYXERR(Debug::GRAPHICS, "filetools(getFormatFromContents)\n" + << "\tCouldn't find a known format!"); + return string(); +} + +} + + string Formats::getFormatFromFile(FileName const & filename) const { if (filename.empty()) return string(); #ifdef HAVE_MAGIC_H - magic_t magic_cookie = magic_open(MAGIC_MIME); - if (magic_cookie) { - string format; - if (magic_load(magic_cookie, NULL) != 0) { - LYXERR(Debug::GRAPHICS, "Formats::getFormatFromFile\n" - << "\tCouldn't load magic database - " - << magic_error(magic_cookie)); - } else { - string mime = magic_file(magic_cookie, - filename.toFilesystemEncoding().c_str()); - mime = token(mime, ';', 0); - // we need our own ps/eps detection - if (mime != "application/postscript") { - Formats::const_iterator cit = - find_if(formatlist.begin(), formatlist.end(), - FormatMimeEqual(mime)); - if (cit != formats.end()) { - LYXERR(Debug::GRAPHICS, "\tgot format from MIME type: " - << mime << " -> " << cit->name()); - format = cit->name(); + if (filename.exists()) { + magic_t magic_cookie = magic_open(MAGIC_MIME); + if (magic_cookie) { + string format; + if (magic_load(magic_cookie, NULL) != 0) { + LYXERR(Debug::GRAPHICS, "Formats::getFormatFromFile\n" + << "\tCouldn't load magic database - " + << magic_error(magic_cookie)); + } else { + string mime = magic_file(magic_cookie, + filename.toFilesystemEncoding().c_str()); + mime = token(mime, ';', 0); + // we need our own ps/eps detection + if ((mime != "application/postscript") && (mime != "text/plain")) { + Formats::const_iterator cit = + find_if(formatlist.begin(), formatlist.end(), + FormatMimeEqual(mime)); + if (cit != formats.end()) { + LYXERR(Debug::GRAPHICS, "\tgot format from MIME type: " + << mime << " -> " << cit->name()); + format = cit->name(); + } } } + magic_close(magic_cookie); + if (!format.empty()) + return format; } - magic_close(magic_cookie); - if (!format.empty()) - return format; } #endif - string const format = filename.guessFormatFromContents(); + string const format = guessFormatFromContents(filename); string const ext = getExtension(filename.absFileName()); if (isZippedFileFormat(format) && !ext.empty()) { string const & fmt_name = formats.getFormatFromExtension(ext); @@ -287,7 +507,7 @@ bool Formats::isZippedFile(support::FileName const & filename) const { return it->second.zipped; string const & format = getFormatFromFile(filename); bool zipped = (format == "gzip" || format == "zip"); - zipped_.insert(pair(fname, ZippedInfo(zipped, timestamp))); + zipped_.insert(make_pair(fname, ZippedInfo(zipped, timestamp))); return zipped; } @@ -488,7 +708,7 @@ bool Formats::edit(Buffer const & buffer, FileName const & filename, // LinkBack files look like PDF, but have the .linkback extension string const ext = getExtension(filename.absFileName()); - if (format_name == "pdf" && ext == "linkback") { + if (format_name == "pdf6" && ext == "linkback") { #ifdef USE_MACOSX_PACKAGING return editLinkBackFile(filename.absFileName().c_str()); #else @@ -574,8 +794,10 @@ string const Formats::extensions(string const & name) const namespace { + typedef Translator FlavorTranslator; + FlavorTranslator initFlavorTranslator() { FlavorTranslator f(OutputParams::LATEX, "latex"); @@ -595,6 +817,7 @@ FlavorTranslator const & flavorTranslator() static FlavorTranslator translator = initFlavorTranslator(); return translator; } + }