Skip to content

EMF image support #196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docx/image/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from docx.image.jpeg import Exif, Jfif
from docx.image.png import Png
from docx.image.tiff import Tiff
from docx.image.emf import Emf


SIGNATURES = (
Expand All @@ -26,4 +27,5 @@
(Tiff, 0, b'MM\x00*'), # big-endian (Motorola) TIFF
(Tiff, 0, b'II*\x00'), # little-endian (Intel) TIFF
(Bmp, 0, b'BM'),
(Emf, 40, b' EMF')
)
1 change: 1 addition & 0 deletions docx/image/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class MIME_TYPE(object):
JPEG = 'image/jpeg'
PNG = 'image/png'
TIFF = 'image/tiff'
EMF = 'image/emf'


class PNG_CHUNK_TYPE(object):
Expand Down
70 changes: 70 additions & 0 deletions docx/image/emf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# encoding: utf-8

from __future__ import absolute_import, division, print_function

from .constants import MIME_TYPE
from .exceptions import InvalidImageStreamError
from .helpers import BIG_ENDIAN, StreamReader
from .image import BaseImageHeader
import struct

class Emf(BaseImageHeader):
"""
Image header parser for PNG images
"""
@property
def content_type(self):
"""
MIME content type for this image, unconditionally `image/png` for
PNG images.
"""
return MIME_TYPE.EMF

@property
def default_ext(self):
"""
Default filename extension, always 'png' for PNG images.
"""
return 'emf'

@classmethod
def from_stream(cls, stream,filename=None):
"""
Return a |Emf| instance having header properties parsed from image in
*stream*.
"""

"""
@0 DWORD iType; // fixed
@4 DWORD nSize; // var
@8 RECTL rclBounds;
@24 RECTL rclFrame; // .01 millimeter units L T R B
@40 DWORD dSignature; // ENHMETA_SIGNATURE = 0x464D4520
DWORD nVersion;
DWORD nBytes;
DWORD nRecords;
WORD nHandles;
WORD sReserved;
DWORD nDescription;
DWORD offDescription;
DWORD nPalEntries;
SIZEL szlDevice;
SIZEL szlMillimeters;
"""
stream.seek(0)
x = stream.read(40)
stream.seek(0)
iType,nSize = struct.unpack("ii",x[0:8])
rclBounds = struct.unpack("iiii",x[8:24])
rclFrame = struct.unpack("iiii",x[24:40])

dpi = 300
horz_dpi = dpi
vert_dpi = dpi
mmwidth = (rclFrame[2]-rclFrame[0])/100.0
mmheight = (rclFrame[3]-rclFrame[1])/100.0
px_width = int(mmwidth*dpi*0.03937008)
px_height = int(mmheight*dpi*0.03937008)

#1 dot/inch = 0.03937008 pixel/millimeter
return cls(px_width,px_height,horz_dpi,vert_dpi)
2 changes: 1 addition & 1 deletion docx/image/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def _ImageHeaderFactory(stream):

def read_32(stream):
stream.seek(0)
return stream.read(32)
return stream.read(64)

header = read_32(stream)
for cls, offset, signature_bytes in SIGNATURES:
Expand Down
Binary file added samples/testaddemf.emf
Binary file not shown.
32 changes: 32 additions & 0 deletions samples/testaddemf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import docx
from docx import Document
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.shared import Mm


def docx_addpicture(document, path, width_mm, caption):
paragraph = document.add_paragraph()
paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = paragraph.add_run()
run.add_picture(path, width=Mm(width_mm))
# DOCX missing bookmarks
# DOCX numbering goes next paragraph (WHY?)
# See Issue: https://github.com/python-openxml/python-docx/issues/138
if caption != "":
paragraph = document.add_paragraph(style="Caption")
p = paragraph._p # this is the actual lxml element for a paragraph
run = paragraph.add_run("Figure ")
fld_xml = ('<w:fldSimple %s w:instr=" SEQ Figure \* ARABIC "/>' %
nsdecls('w'))
fldSimple = parse_xml(fld_xml) # this goes AFTER the field (!)
#p.addnext(fldSimple)
paragraph.add_run(" " + caption)


document = Document()

docx_addpicture(document,"testaddemf.emf", width_mm=100,caption="Groups Implementation")

document.save("testaddemf.docx")