msodumper/wmfrecord.py |  345 +++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 291 insertions(+), 54 deletions(-)

New commits:
commit b88b38cef5e42137edf8441325765a8ac35aa8c3
Author:     Hossein <hoss...@libreoffice.org>
AuthorDate: Mon Sep 20 01:18:20 2021 +0200
Commit:     Hossein <hoss...@libreoffice.org>
CommitDate: Mon Sep 20 01:26:04 2021 +0200

    Added dump() for 17 wmf records in wmf-dump.py
    
    Added dump() for 17 wmf records to complete the support for
    test/wmf/pass/tdf88163.wmf file in wmf-dump.py. 58 records of wmf
    still need dump().
    
    List of records:
    
    1.  SetMapMode
    2.  SetPolyFillMode
    3.  SetTextColor
    4.  SetBkColor
    5.  MoveTo
    6.  Polygon
    7.  SetWindowOrg
    8.  SetWindowExt
    9.  TextOut
    10. PolyPolygon
    11. SelectObject
    12. DeleteObject
    13. PenRecord
    14. CreatePenIndirect
    15. LogBrush
    16. CreateBrushIndirect
    17. EndOfFile
    
    Change-Id: I1a3d2c30aa6bba01891662b8641a1213d3f5d16f
    Reviewed-on: https://gerrit.libreoffice.org/c/mso-dumper/+/122333
    Tested-by: Hossein <hoss...@libreoffice.org>
    Reviewed-by: Hossein <hoss...@libreoffice.org>

diff --git a/msodumper/wmfrecord.py b/msodumper/wmfrecord.py
index cdc0581..272ba79 100644
--- a/msodumper/wmfrecord.py
+++ b/msodumper/wmfrecord.py
@@ -265,6 +265,17 @@ PaletteEntryFlag = {
     0x04: "PC_NOCOLLAPSE",
 }
 
+PitchFont = {
+    0: "DEFAULT_PITCH",
+    1: "FIXED_PITCH",
+    2: "VARIABLE_PITCH",
+}
+
+PolyFillMode = {
+    0x0001: "ALTERNATE",
+    0x0002: "WINDING",
+}
+
 PostScriptCap = {
     -2: "PostScriptNotSet",
     0: "PostScriptFlatCap",
@@ -567,6 +578,26 @@ RasterPolishMap = {
 }
 
 
+PenStyle = {
+    0x0000: "PS_COSMETIC",
+    0x0000: "PS_ENDCAP_ROUND",
+    0x0000: "PS_JOIN_ROUND",
+    0x0000: "PS_SOLID",
+    0x0001: "PS_DASH",
+    0x0002: "PS_DOT",
+    0x0003: "PS_DASHDOT",
+    0x0004: "PS_DASHDOTDOT",
+    0x0005: "PS_NULL",
+    0x0006: "PS_INSIDEFRAME",
+    0x0007: "PS_USERSTYLE",
+    0x0008: "PS_ALTERNATE",
+    0x0100: "PS_ENDCAP_SQUARE",
+    0x0200: "PS_ENDCAP_FLAT",
+    0x1000: "PS_JOIN_BEVEL",
+    0x2000: "PS_JOIN_MITER",
+}
+
+
 class WMFStream(BinaryStream):
     def __init__(self, bytes):
         BinaryStream.__init__(self, bytes)
@@ -776,7 +807,7 @@ class PlaceableHeader(WMFRecord):
         self.printAndSet("HWmf", self.readuInt16())
         Rect(self, "BoundingBox").dump()
         self.printAndSet("Inch", self.readuInt16(), hexdump=False)
-        self.printAndSet("Reserved", self.readuInt32())
+        self.printAndSet("Reserved", self.readuInt32(), hexdump=False)
         self.printAndSet("Checksum", self.readuInt16())
         assert self.pos == dataPos + self.Size
         self.parent.pos = self.pos
@@ -834,7 +865,6 @@ class SetBkMode(WMFRecord):
             self.name = "setbkmode"
 
     def dump(self):
-        pass
         dataPos = self.pos
         print('<%s type="SetBkMode">' % self.name)
         self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
@@ -848,12 +878,22 @@ class SetBkMode(WMFRecord):
 
 
 class SetMapMode(WMFRecord):
-    def __init__(self, parent):
+    """The META_SETMAPMODE record is used to define the mapping mode in the 
playback device context."""
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "selectobject"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="SetTextAlign">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("MapMode", self.readuInt16(), hexdump=False)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class SetROP2(WMFRecord):
@@ -875,12 +915,24 @@ class SetRelAbs(WMFRecord):
 
 
 class SetPolyFillMode(WMFRecord):
-    def __init__(self, parent):
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "setpolyfillmode"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="SetPolyFillMode">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("PolyFillMode", self.readuInt16(), dict=PolyFillMode)
+        # Check optional reserved value if the size shows that it exists
+        if self.RecordSize == 5:
+            self.printAndSet("Reserved", self.readuInt16(), hexdump=False)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class SetStretchBltMode(WMFRecord):
@@ -937,40 +989,62 @@ class SetLayout(WMFRecord):
         pass
 
 
-class SetTextColor(WMFRecord):
-    def __init__(self, parent):
-        WMFRecord.__init__(self, parent)
-
-    def dump(self):
-        print("<todo/>")
-        pass
-
-
 class SetBkColor(WMFRecord):
-    def __init__(self, parent):
+    """The META_SETBKCOLOR record is used to set the background color. If the 
specified color
+    is not possible in the device, the nearest physical color is used."""
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "setbkcolor"
 
     def dump(self):
-        print("<todo/>")
         pass
+        dataPos = self.pos
+        print('<%s type="SetBkColor">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        ColorRef(self, "ColorRef").dump()
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class SetTextColor(WMFRecord):
-    def __init__(self, parent):
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "settextcolor"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="SetTextColor">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        ColorRef(self, "ColorRef").dump()
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class MoveTo(WMFRecord):
-    def __init__(self, parent):
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "moveto"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="MoveTo">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("Y", self.readInt16(), hexdump=False)
+        self.printAndSet("X", self.readInt16(), hexdump=False)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class OffsetClipRgn(WMFRecord):
@@ -1010,12 +1084,23 @@ class SelectPalette(WMFRecord):
 
 
 class Polygon(WMFRecord):
-    def __init__(self, parent):
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "polygon"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="Polygon">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("NumberOfPoints", self.readInt16(), hexdump=False)
+        for i in range(self.NumberOfPoints):
+            PointS(self, "aPoint%d" % i).dump()
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class Polyline(WMFRecord):
@@ -1037,21 +1122,44 @@ class SetTextJustification(WMFRecord):
 
 
 class SetWindowOrg(WMFRecord):
-    def __init__(self, parent):
+    """The META_SETWINDOWORG record is used to define the output window 
origin."""
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "setwindoworg"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="SetWindowOrg">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("Y", self.readInt16(), hexdump=False)
+        self.printAndSet("X", self.readInt16(), hexdump=False)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
-class SetWindowExt:
-    def __init__(self, parent):
+class SetWindowExt(WMFRecord):
+    """The META_SETWINDOWEXT Record is used to define the extents of the output
+    window, both for the horizontal and vertical dimensions."""
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "setwindoworg"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="SetWindowExt">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("Y", self.readInt16(), hexdump=False)
+        self.printAndSet("X", self.readInt16(), hexdump=False)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class SetViewportOrg(WMFRecord):
@@ -1145,21 +1253,63 @@ class AnimatePalette(WMFRecord):
 
 
 class TextOut(WMFRecord):
-    def __init__(self, parent):
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "textout"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="TextOut">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("StringLength", self.readInt16(), hexdump=False)
+        size = self.StringLength + self.StringLength % 2
+        StringByte = self.readBytes(size)
+        # Use bytes until chr(0) (null byte)
+        self.String = ""
+        for i in range(self.StringLength):
+            if StringByte[i] == 0:
+                break
+            self.String += chr(StringByte[i])
+
+        print('<String value="%s"/>' % (self.String))
+        self.printAndSet("YStart", self.readInt16(), hexdump=False)
+        self.printAndSet("XStart", self.readInt16(), hexdump=False)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class PolyPolygon(WMFRecord):
-    def __init__(self, parent):
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "polypolygon"
 
     def dump(self):
-        print("<todo/>")
         pass
+        dataPos = self.pos
+        print('<%s type="PolyPolygon">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("NumberOfPolygons", self.readuInt16(), hexdump=False)
+        print('<PolygonPointCounts>')
+        self.allPoints = 0
+        for i in range(self.NumberOfPolygons):
+            self.printAndSet("aPointsPerPolygon%d" % i, self.readuInt16(), 
hexdump=False)
+        print('</PolygonPointCounts>')
+        for i in range(self.NumberOfPolygons):
+            points = getattr(self, "aPointsPerPolygon%d" % i)
+            print('<Polygon%d>' % i)
+            for j in range(points):
+                PointS(self, "aPoint%d" % j).dump()
+            print('</Polygon%d>' % i)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class ExtFloodFill(WMFRecord):
@@ -1343,16 +1493,26 @@ class SelectClipRgn(WMFRecord):
 
 
 class SelectObject(WMFRecord):
-    def __init__(self, parent):
+    """The META_SELECTOBJECT record is used to specify a graphics object for 
the playback device context."""
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "selectobject"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="SelectObject">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("ObjectIndex", self.readuInt16(), hexdump=False)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class SetTextAlign(WMFRecord):
-    """The SetTextAlign record is used to define the text alignment"""
+    """The META_SETTEXTALIGN record is used to define the text alignment"""
     def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
         if name:
@@ -1446,12 +1606,21 @@ class StretchDIBits(WMFRecord):
 
 
 class DeleteObject(WMFRecord):
-    def __init__(self, parent):
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "deleteobject"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="SelectObject">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        self.printAndSet("ObjectIndex", self.readuInt16(), hexdump=False)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class CreatePalette(WMFRecord):
@@ -1472,13 +1641,40 @@ class CreatePatternBrush(WMFRecord):
         pass
 
 
+class PenRecord(WMFRecord):
+    """The Pen object specifies the style, width, and color of an logical 
pen."""
+    def __init__(self, parent, name):
+        WMFRecord.__init__(self, parent)
+        self.name = name
+
+    def dump(self):
+        print('<%s type="Pen">' % self.name)
+        self.printAndSet("PenStyle", self.readuInt16(), dict=PenStyle)
+        self.printAndSet("Width", self.readuInt32())
+        ColorRef(self, "ColorRef").dump()
+        print('</%s>' % self.name)
+        self.parent.pos = self.pos
+
+
 class CreatePenIndirect(WMFRecord):
-    def __init__(self, parent):
+    """The META_CREATEPENINDIRECT record is used to create a Pen object"""
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "createpenindirect"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="CreatePenIndirect">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        # Check optional reserved value if the size shows that it exists
+        PenRecord(self, "Pen").dump()
+        print('</%s>' % self.name)
+        # RecordSize is described in words, so we should double for bytes
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class CreateFontIndirect(WMFRecord):
@@ -1541,13 +1737,37 @@ class Font(WMFRecord):
         self.parent.pos = self.pos
 
 
+class LogBrush(WMFRecord):
+    """The Brush object defines the style, color, and pattern of a 
device-independent brush."""
+    def __init__(self, parent, name):
+        WMFRecord.__init__(self, parent)
+        self.name = name
+
+    def dump(self):
+        print('<%s>' % self.name)
+        self.printAndSet("BrushStyle", self.readuInt16(), dict=BrushStyle)
+        ColorRef(self, "ColorRef").dump()
+        self.printAndSet("BrushHatch", self.readuInt16(), dict=HatchStyle)
+        print('</%s>' % self.name)
+        self.parent.pos = self.pos
+
+
 class CreateBrushIndirect(WMFRecord):
-    def __init__(self, parent):
+    def __init__(self, parent, name=None):
         WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "createbrushindirect"
 
     def dump(self):
-        print("<todo/>")
-        pass
+        dataPos = self.pos
+        print('<%s type="CreateBrushIndirect">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        LogBrush(self, "LogBrush").dump()
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
 
 
 class CreateRectRgn(WMFRecord):
@@ -1559,11 +1779,28 @@ class CreateRectRgn(WMFRecord):
         pass
 
 
+class EndOfFile(WMFRecord):
+    def __init__(self, parent, name=None):
+        WMFRecord.__init__(self, parent)
+        if name:
+            self.name = name
+        else:
+            self.name = "eof"
+
+    def dump(self):
+        dataPos = self.pos
+        print('<%s type="EndOfFile">' % self.name)
+        self.printAndSet("RecordSize", self.readuInt32(), hexdump=False)
+        self.printAndSet("RecordFunction", self.readuInt16(), hexdump=True)
+        print('</%s>' % self.name)
+        assert self.pos == dataPos + self.RecordSize * 2
+
+
 # GDI Functions: https://docs.microsoft.com/en-us/windows/win32/api/_gdi/
 # Wine API / GDI: https://source.winehq.org/WineAPI/gdi.html
 """The RecordType enumeration defines values that uniquely identify WMF 
records."""
 RecordType = {
-    0x0000: ['META_EOF'],
+    0x0000: ['META_EOF', EndOfFile],
     0x0035: ['META_REALIZEPALETTE', RealizePalette],
     0x0037: ['META_SETPALENTRIES', SetPaletteEntries],
     0x0102: ['META_SETBKMODE', SetBkMode],

Reply via email to