DocxImageExtractor.java

1
package pro.verron.officestamper.core;
2
3
import jakarta.xml.bind.JAXBElement;
4
import org.docx4j.dml.picture.Pic;
5
import org.docx4j.dml.wordprocessingDrawing.Inline;
6
import org.docx4j.openpackaging.exceptions.Docx4JException;
7
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
8
import org.docx4j.wml.Drawing;
9
import org.docx4j.wml.R;
10
import pro.verron.officestamper.api.OfficeStamperException;
11
12
import java.io.IOException;
13
import java.io.InputStream;
14
import java.util.Arrays;
15
16
/// Extracts images from a docx document.
17
///
18
/// @author Joseph Verron
19
/// @version ${version}
20
/// @since 1.4.7
21
public class DocxImageExtractor {
22
23
    private final WordprocessingMLPackage wordprocessingMLPackage;
24
25
    /// Creates a new image extractor for the given docx document.
26
    ///
27
    /// @param wordprocessingMLPackage the docx document to extract images from.
28
    public DocxImageExtractor(WordprocessingMLPackage wordprocessingMLPackage) {
29
        this.wordprocessingMLPackage = wordprocessingMLPackage;
30
    }
31
32
    /// Extract an image bytes from an embedded image run.
33
    ///
34
    /// @param run run containing the embedded drawing.
35
    byte[] getRunDrawingData(R run) {
36
        String imageRelId = getPic(run).getBlipFill()
37
                                       .getBlip()
38
                                       .getEmbed();
39
        String imageRelPartName = getImageRelPartName(imageRelId);
40
        long size = getImageSize(imageRelPartName);
41
        InputStream stream = getImageStream(imageRelPartName);
42 1 1. getRunDrawingData : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingData → KILLED
        return streamToByteArray(size, stream);
43
    }
44
45
    private static Pic getPic(R run) {
46
        var content = run.getContent();
47
        for (Object runContent : content) {
48 2 1. getPic : negated conditional → KILLED
2. getPic : negated conditional → KILLED
            if (runContent instanceof JAXBElement<?> runElement && runElement.getValue() instanceof Drawing drawing) {
49 1 1. getPic : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getPic → KILLED
                return getPic(drawing);
50
            }
51
        }
52
        throw new OfficeStamperException("Run drawing not found !");
53
    }
54
55
    private String getImageRelPartName(String imageRelId) {
56
        // TODO: find a better way to find image rel part name in source part store
57 1 1. getImageRelPartName : replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getImageRelPartName → KILLED
        return wordprocessingMLPackage.getMainDocumentPart()
58
                                      .getRelationshipsPart()
59
                                      .getPart(imageRelId)
60
                                      .getPartName()
61
                                      .getName()
62
                                      .substring(1);
63
    }
64
65
    private long getImageSize(String imageRelPartName) {
66
        try {
67 1 1. getImageSize : replaced long return with 0 for pro/verron/officestamper/core/DocxImageExtractor::getImageSize → KILLED
            return wordprocessingMLPackage.getSourcePartStore()
68
                                          .getPartSize(imageRelPartName);
69
        } catch (Docx4JException e) {
70
            throw new OfficeStamperException(e);
71
        }
72
    }
73
74
    private InputStream getImageStream(String imageRelPartName) {
75
        try {
76 1 1. getImageStream : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getImageStream → KILLED
            return wordprocessingMLPackage.getSourcePartStore()
77
                                          .loadPart(imageRelPartName);
78
        } catch (Docx4JException e) {
79
            throw new OfficeStamperException(e);
80
        }
81
    }
82
83
    /// Converts an InputStream to a byte array.
84
    ///
85
    /// @param size expected size of the byte array.
86
    /// @param is   input stream to read data from.
87
    ///
88
    /// @return the data from the input stream.
89
    private static byte[] streamToByteArray(long size, InputStream is) {
90 2 1. streamToByteArray : changed conditional boundary → SURVIVED
2. streamToByteArray : negated conditional → KILLED
        if (size > Integer.MAX_VALUE) throw new OfficeStamperException("Image size exceeds maximum allowed (2GB)");
91
92
        int intSize = (int) size;
93
        byte[] data = new byte[intSize];
94
        int numRead = tryRead(is, data);
95 1 1. streamToByteArray : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::streamToByteArray → KILLED
        return Arrays.copyOfRange(data, 0, numRead);
96
    }
97
98
    private static Pic getPic(Drawing drawing) {
99
        var anchorOrInline = drawing.getAnchorOrInline();
100 1 1. getPic : negated conditional → KILLED
        if (anchorOrInline.isEmpty()) throw new OfficeStamperException("Anchor or Inline is empty !");
101 1 1. getPic : negated conditional → KILLED
        if (!(anchorOrInline.getFirst() instanceof Inline inline))
102
            throw new OfficeStamperException("Don't know how to process anchor !");
103 1 1. getPic : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getPic → KILLED
        return inline.getGraphic()
104
                     .getGraphicData()
105
                     .getPic();
106
    }
107
108
    private static int tryRead(InputStream is, byte[] data) {
109
        try {
110 1 1. tryRead : replaced int return with 0 for pro/verron/officestamper/core/DocxImageExtractor::tryRead → KILLED
            return is.read(data);
111
        } catch (IOException e) {
112
            throw new OfficeStamperException(e);
113
        }
114
    }
115
116
    /// Extract the width of the image from an embedded image run.
117
    ///
118
    /// @param run run containing the embedded drawing.
119
    ///
120
    /// @return a [Integer] object
121
    public Integer getRunDrawingMaxWidth(R run) {
122 1 1. getRunDrawingMaxWidth : replaced Integer return value with 0 for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingMaxWidth → SURVIVED
        return (int) getPic(run).getSpPr()
123
                                .getXfrm()
124
                                .getExt()
125
                                .getCx();
126
    }
127
}

Mutations

42

1.1
Location : getRunDrawingData
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingData → KILLED

48

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
negated conditional → KILLED

2.2
Location : getPic
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
negated conditional → KILLED

49

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getPic → KILLED

57

1.1
Location : getImageRelPartName
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getImageRelPartName → KILLED

67

1.1
Location : getImageSize
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
replaced long return with 0 for pro/verron/officestamper/core/DocxImageExtractor::getImageSize → KILLED

76

1.1
Location : getImageStream
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getImageStream → KILLED

90

1.1
Location : streamToByteArray
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : streamToByteArray
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
negated conditional → KILLED

95

1.1
Location : streamToByteArray
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::streamToByteArray → KILLED

100

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
negated conditional → KILLED

101

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
negated conditional → KILLED

103

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getPic → KILLED

110

1.1
Location : tryRead
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPartTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPartTest]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#10]
replaced int return with 0 for pro/verron/officestamper/core/DocxImageExtractor::tryRead → KILLED

122

1.1
Location : getRunDrawingMaxWidth
Killed by : none
replaced Integer return value with 0 for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingMaxWidth → SURVIVED
Covering tests

Active mutators

Tests examined


Report generated by PIT 1.21.0