Пример #1
0
    WriteMethod ExifParser::encode(
              Blob&     blob,
        const byte*     pData,
              uint32_t  size,
              ByteOrder byteOrder,
        const ExifData& exifData
    )
    {
        ExifData ed = exifData;

        // Delete IFD0 tags that are "not recorded" in compressed images
        // Reference: Exif 2.2 specs, 4.6.8 Tag Support Levels, section A
        static const char* filteredIfd0Tags[] = {
            "Exif.Image.PhotometricInterpretation",
            "Exif.Image.StripOffsets",
            "Exif.Image.RowsPerStrip",
            "Exif.Image.StripByteCounts",
            "Exif.Image.JPEGInterchangeFormat",
            "Exif.Image.JPEGInterchangeFormatLength",
            "Exif.Image.SubIFDs"
        };
        for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfd0Tags); ++i) {
            ExifData::iterator pos = ed.findKey(ExifKey(filteredIfd0Tags[i]));
            if (pos != ed.end()) {
#ifdef DEBUG
                std::cerr << "Warning: Exif tag " << pos->key() << " not encoded\n";
#endif
                ed.erase(pos);
            }
        }

        // Delete IFDs which do not occur in JPEGs
        static const IfdId filteredIfds[] = {
            subImage1Id,
            subImage2Id,
            subImage3Id,
            subImage4Id,
            subImage5Id,
            subImage6Id,
            subImage7Id,
            subImage8Id,
            subImage9Id,
            subThumb1Id,
            panaRawId,
            ifd2Id,
            ifd3Id
        };
        for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfds); ++i) {
#ifdef DEBUG
            std::cerr << "Warning: Exif IFD " << filteredIfds[i] << " not encoded\n";
#endif
            eraseIfd(ed, filteredIfds[i]);
        }

        // IPTC and XMP are stored elsewhere, not in the Exif APP1 segment.
        IptcData emptyIptc;
        XmpData  emptyXmp;

        // Encode and check if the result fits into a JPEG Exif APP1 segment
        MemIo mio1;
        std::auto_ptr<TiffHeaderBase> header(new TiffHeader(byteOrder, 0x00000008, false));
        WriteMethod wm = TiffParserWorker::encode(mio1,
                                                  pData,
                                                  size,
                                                  ed,
                                                  emptyIptc,
                                                  emptyXmp,
                                                  Tag::root,
                                                  TiffMapping::findEncoder,
                                                  header.get(),
                                                  0);
        if (mio1.size() <= 65527) {
            append(blob, mio1.mmap(), mio1.size());
            return wm;
        }

        // If it doesn't fit, remove additional tags

        // Delete preview tags if the preview is larger than 32kB.
        // Todo: Enhance preview classes to be able to write and delete previews and use that instead.
        // Table must be sorted by preview, the first tag in each group is the size
        static const PreviewTags filteredPvTags[] = {
            { pttLen, "Exif.Minolta.ThumbnailLength"                  },
            { pttTag, "Exif.Minolta.ThumbnailOffset"                  },
            { pttLen, "Exif.Minolta.Thumbnail"                        },
            { pttLen, "Exif.NikonPreview.JPEGInterchangeFormatLength" },
            { pttIfd, "NikonPreview"                                  },
            { pttLen, "Exif.Olympus.ThumbnailLength"                  },
            { pttTag, "Exif.Olympus.ThumbnailOffset"                  },
            { pttLen, "Exif.Olympus.ThumbnailImage"                   },
            { pttLen, "Exif.Olympus.Thumbnail"                        },
            { pttLen, "Exif.Olympus2.ThumbnailLength"                 },
            { pttTag, "Exif.Olympus2.ThumbnailOffset"                 },
            { pttLen, "Exif.Olympus2.ThumbnailImage"                  },
            { pttLen, "Exif.Olympus2.Thumbnail"                       },
            { pttLen, "Exif.OlympusCs.PreviewImageLength"             },
            { pttTag, "Exif.OlympusCs.PreviewImageStart"              },
            { pttTag, "Exif.OlympusCs.PreviewImageValid"              },
            { pttLen, "Exif.Pentax.PreviewLength"                     },
            { pttTag, "Exif.Pentax.PreviewOffset"                     },
            { pttTag, "Exif.Pentax.PreviewResolution"                 },
            { pttLen, "Exif.PentaxDng.PreviewLength"                  },
            { pttTag, "Exif.PentaxDng.PreviewOffset"                  },
            { pttTag, "Exif.PentaxDng.PreviewResolution"              },
            { pttLen, "Exif.SamsungPreview.JPEGInterchangeFormatLength" },
            { pttIfd, "SamsungPreview"                                },
            { pttLen, "Exif.Thumbnail.StripByteCounts"                },
            { pttIfd, "Thumbnail"                                     },
            { pttLen, "Exif.Thumbnail.JPEGInterchangeFormatLength"    },
            { pttIfd, "Thumbnail"                                     }
        };
        bool delTags = false;
        ExifData::iterator pos;
        for (unsigned int i = 0; i < EXV_COUNTOF(filteredPvTags); ++i) {
            switch (filteredPvTags[i].ptt_) {
            case pttLen:
                delTags = false;
                pos = ed.findKey(ExifKey(filteredPvTags[i].key_));
                if (pos != ed.end() && sumToLong(*pos) > 32768) {
                    delTags = true;
#ifndef SUPPRESS_WARNINGS
                    EXV_WARNING << "Exif tag " << pos->key() << " not encoded\n";
#endif
                    ed.erase(pos);
                }
                break;
            case pttTag:
                if (delTags) {
                    pos = ed.findKey(ExifKey(filteredPvTags[i].key_));
                    if (pos != ed.end()) {
#ifndef SUPPRESS_WARNINGS
                        EXV_WARNING << "Exif tag " << pos->key() << " not encoded\n";
#endif
                        ed.erase(pos);
                    }
                }
                break;
            case pttIfd:
                if (delTags) {
#ifndef SUPPRESS_WARNINGS
                    EXV_WARNING << "Exif IFD " << filteredPvTags[i].key_ << " not encoded\n";
#endif
                    eraseIfd(ed, Internal::groupId(filteredPvTags[i].key_));
                }
                break;
            }
        }

        // Delete unknown tags larger than 4kB and known tags larger than 40kB.
        for (ExifData::iterator pos = ed.begin(); pos != ed.end(); ) {
            if (   (pos->size() > 4096 && pos->tagName().substr(0, 2) == "0x")
                || pos->size() > 40960) {
#ifndef SUPPRESS_WARNINGS
                EXV_WARNING << "Exif tag " << pos->key() << " not encoded\n";
#endif
                pos = ed.erase(pos);
            }
            else {
                ++pos;
            }
        }

        // Encode the remaining Exif tags again, don't care if it fits this time
        MemIo mio2;
        wm = TiffParserWorker::encode(mio2,
                                      pData,
                                      size,
                                      ed,
                                      emptyIptc,
                                      emptyXmp,
                                      Tag::root,
                                      TiffMapping::findEncoder,
                                      header.get(),
                                      0);
        append(blob, mio2.mmap(), mio2.size());
#ifdef DEBUG
        if (wm == wmIntrusive) {
            std::cerr << "SIZE OF EXIF DATA IS " << std::dec << mio2.size() << " BYTES\n";
        }
        else {
            std::cerr << "SIZE DOESN'T MATTER, NON-INTRUSIVE WRITING USED\n";
        }
#endif
        return wm;

    } // ExifParser::encode
Пример #2
0
    void Rw2Image::readMetadata()
    {
#ifdef DEBUG
        std::cerr << "Reading RW2 file " << io_->path() << "\n";
#endif
        if (io_->open() != 0) {
            throw Error(9, io_->path(), strError());
        }
        IoCloser closer(*io_);
        // Ensure that this is the correct image type
        if (!isRw2Type(*io_, false)) {
            if (io_->error() || io_->eof()) throw Error(14);
            throw Error(3, "RW2");
        }
        clearMetadata();
        std::ofstream devnull;
        printStructure(devnull, kpsRecursive, 0);
        ByteOrder bo = Rw2Parser::decode(exifData_,
                                         iptcData_,
                                         xmpData_,
                                         io_->mmap(),
                                         io_->size());
        setByteOrder(bo);

        // A lot more metadata is hidden in the embedded preview image
        // Todo: This should go into the Rw2Parser, but for that it needs the Image
        PreviewManager loader(*this);
        PreviewPropertiesList list = loader.getPreviewProperties();
        // Todo: What if there are more preview images?
        if (list.size() > 1) {
#ifndef SUPPRESS_WARNINGS
            EXV_WARNING << "RW2 image contains more than one preview. None used.\n";
#endif
        }
        if (list.size() != 1) return;
        ExifData exifData;
        PreviewImage preview = loader.getPreviewImage(*list.begin());
        Image::AutoPtr image = ImageFactory::open(preview.pData(), preview.size());
        if (image.get() == 0) {
#ifndef SUPPRESS_WARNINGS
            EXV_WARNING << "Failed to open RW2 preview image.\n";
#endif
            return;
        }
        image->readMetadata();
        ExifData& prevData = image->exifData();
        if (!prevData.empty()) {
            // Filter duplicate tags
            for (ExifData::const_iterator pos = exifData_.begin(); pos != exifData_.end(); ++pos) {
                if (pos->ifdId() == panaRawId) continue;
                ExifData::iterator dup = prevData.findKey(ExifKey(pos->key()));
                if (dup != prevData.end()) {
#ifdef DEBUG
                    std::cerr << "Filtering duplicate tag " << pos->key()
                              << " (values '" << pos->value()
                              << "' and '" << dup->value() << "')\n";
#endif
                    prevData.erase(dup);
                }
            }
        }
        // Remove tags not applicable for raw images
        static const char* filteredTags[] = {
            "Exif.Photo.ComponentsConfiguration",
            "Exif.Photo.CompressedBitsPerPixel",
            "Exif.Panasonic.ColorEffect",
            "Exif.Panasonic.Contrast",
            "Exif.Panasonic.NoiseReduction",
            "Exif.Panasonic.ColorMode",
            "Exif.Panasonic.OpticalZoomMode",
            "Exif.Panasonic.Contrast",
            "Exif.Panasonic.Saturation",
            "Exif.Panasonic.Sharpness",
            "Exif.Panasonic.FilmMode",
            "Exif.Panasonic.SceneMode",
            "Exif.Panasonic.WBRedLevel",
            "Exif.Panasonic.WBGreenLevel",
            "Exif.Panasonic.WBBlueLevel",
            "Exif.Photo.ColorSpace",
            "Exif.Photo.PixelXDimension",
            "Exif.Photo.PixelYDimension",
            "Exif.Photo.SceneType",
            "Exif.Photo.CustomRendered",
            "Exif.Photo.DigitalZoomRatio",
            "Exif.Photo.SceneCaptureType",
            "Exif.Photo.GainControl",
            "Exif.Photo.Contrast",
            "Exif.Photo.Saturation",
            "Exif.Photo.Sharpness",
            "Exif.Image.PrintImageMatching",
            "Exif.Image.YCbCrPositioning"
        };
        for (unsigned int i = 0; i < EXV_COUNTOF(filteredTags); ++i) {
            ExifData::iterator pos = prevData.findKey(ExifKey(filteredTags[i]));
            if (pos != prevData.end()) {
#ifdef DEBUG
                std::cerr << "Exif tag " << pos->key() << " removed\n";
#endif
                prevData.erase(pos);
            }
        }

        // Add the remaining tags
        for (ExifData::const_iterator pos = prevData.begin(); pos != prevData.end(); ++pos) {
            exifData_.add(*pos);
        }

    } // Rw2Image::readMetadata