When using the draw appearance my images are not getting a orientation tag it seems. I have a script that reads the photo orientation and it is stopping on these images from 123.
if hasattr(image, '_getexif'):
orientation = image._getexif().get(274, 1) # 274 = Orientation
else:
orientation = 1
in fill_page_with_image
orientation = image._getexif().get(274, 1) # 274 = Orientation
AttributeError: 'NoneType' object has no attribute 'get'
Anyone seen this before?
Thanks
Does it work with all pictures, or just a few? Can you post the rest of your code?
It worked on our entire dir which is about 45,000 photos. The drawings from 123 were the only photos that had an issue. Regular 123 photos were fine.
Here is the code. It stops on the line I posted. Seems like an exif issue?
if hasattr(image, '_getexif'):
orientation = image._getexif().get(274, 1) # 274 = Orientation
else:
orientation = 1
in fill_page_with_image
orientation = image._getexif().get(274, 1) # 274 = Orientation
AttributeError: 'NoneType' object has no attribute 'get'
thanks
# Functions---------------------------------------------------------
def fill_page_with_image(path, canvas):
"""
Given the path to an image and a reportlab canvas, fill the current page
with the image.
This function takes into consideration EXIF orientation information (making
it compatible with photos taken from iOS devices).
This function makes use of ``canvas.setPageRotation()`` and
``canvas.setPageSize()`` which will affect subsequent pages, so be sure to
reset them to appropriate values after calling this function.
:param path: filesystem path to an image
:param canvas: ``reportlab.canvas.Canvas`` object
"""
from PIL import Image
page_width, page_height = canvas._pagesize
image = Image.open(path)
image_width, image_height = image.size
# Plot Drawings are failing here so added a check
if hasattr(image, '_getexif') and image._getexif() is not None:
#if hasattr(image, '_getexif'):
orientation = image._getexif().get(274, 1) # 274 = Orientation
else:
orientation = 1
# These are the possible values for the Orientation EXIF attribute:
ORIENTATIONS = {
1: "Horizontal (normal)",
2: "Mirrored horizontal",
3: "Rotated 180",
4: "Mirrored vertical",
5: "Mirrored horizontal then rotated 90 CCW",
6: "Rotated 90 CW",
7: "Mirrored horizontal then rotated 90 CW",
8: "Rotated 90 CCW",
}
draw_width, draw_height = page_width-10, page_height-10
if orientation == 1:
canvas.setPageRotation(0)
elif orientation == 3:
canvas.setPageRotation(180)
elif orientation == 6:
image_width, image_height = image_height, image_width
draw_width, draw_height = page_height-10, page_width-10
canvas.setPageRotation(90)
elif orientation == 8:
image_width, image_height = image_height, image_width
draw_width, draw_height = page_height-10, page_width-10
canvas.setPageRotation(270)
else:
raise ValueError("Unsupported image orientation '%s'."
% ORIENTATIONS[orientation])
if image_width > image_height:
page_width, page_height = page_height, page_width # flip width/height
draw_width, draw_height = draw_height, draw_width
canvas.setPageSize((page_width, page_height))
canvas.drawImage(path, 0, 0, width=draw_width, height=draw_height,
preserveAspectRatio=True)
# End Functions -----------------------------------------------------------
# Get a list of PrimaryKeys from a supplied text file
pkFileLink = open(pkFile, 'r')
pkList = pkFileLink.read().splitlines()
pkFileLink.close()
#Test PK 18112809100042272018-09-01
#pkList = ["13072013104551182013-09-01"]
# make a report for each PrimaryKey
for pk in pkList:
#pdfOut = canvas.Canvas(outputFolder + "\\" + "Revisit Report for " + pk + ".pdf")
print "Building Revisit Report for " + pk
# Join to get
arcpy.MakeFeatureLayer_management(terraSDE, "terraLayer")
arcpy.AddJoin_management("terraLayer", "PrimaryKey", plotsSDE, "PrimaryKey")
fields = [terraFC + '.PrimaryKey', terraFC + '.PlotID', terraFC + '.Latitude_NAD83', terraFC + '.Longitude_NAD83', terraFC + ".State", plotsFC + ".Elevation", plotsFC + ".Slope", \
plotsFC + ".Aspect", plotsFC + ".LandscapeType", plotsFC + ".LandscapeTypeSecondary", plotsFC + ".EcolSite", plotsFC + ".Soil", plotsFC + ".Directions"]
whereClause = terraFC + ".PrimaryKey = '" + pk + "'"
# Print the Summary Info on Page 1
with arcpy.da.SearchCursor("terraLayer", fields, whereClause) as cursor:
for row in cursor:
# Start the PDF
pdfOut = canvas.Canvas(outputFolder + "\\" + "Revisit Report for " + row[1] + ".pdf")
# grad for photo part later
photoState = row[4]
photoKey = row[0]
plotID = row[1]
lineOffset = 760
pdfOut.drawString(200,792,"Revist report for PlotID " + row[1])
pdfOut.drawString(20,lineOffset,"PrimaryKey: " + row[0])
pdfOut.drawString(20,lineOffset-20,"Latitude_NAD83: " + str(row[2]))
pdfOut.drawString(20,lineOffset-40,"Longitude_NAD83: " + str(row[3]))
pdfOut.drawString(20,lineOffset-60,"State: " + row[4])
pdfOut.drawString(20,lineOffset-80,"Elevation Meters: " + str(int(row[5])))
pdfOut.drawString(20,lineOffset-100,"Slope Percent: " + str(row[6]))
pdfOut.drawString(20,lineOffset-120,"Aspect Degrees: " + row[7])
pdfOut.drawString(20,lineOffset-140,"LandscapeType: " + str(row[8]))
pdfOut.drawString(20,lineOffset-160,"LandscapeTypeSecondary: " + str(row[9]))
pdfOut.drawString(20,lineOffset-180,"EcolSite: " + row[10])
pdfOut.drawString(20,lineOffset-200,"Soil: " + str(row[11]))
pdfOut.drawString(20,lineOffset-220,"Directions: ")
# have to worry about long strings here
lineOffset = lineOffset-240
for line in textwrap.wrap(str(row[12]), 90):
pdfOut.drawString(30, lineOffset, str(line))
lineOffset -= 20
arcpy.RemoveJoin_management("terraLayer")
arcpy.Delete_management("terraLayer")
# end page 1
pdfOut.showPage()
#----Page2
# Print out the line info on Page 2
# Just get PrimaryKey records
whereClause = "PrimaryKey = '" + pk + "'"
arcpy.MakeTableView_management(linesSDE, "linesLayer", whereClause)
# Join lines to LPIHeader
arcpy.AddJoin_management("linesLayer", "LineKey", lpiheaderSDE, "LineKey")
fields = [linesFC + ".LineID", linesFC + ".LatitudeStart", linesFC + ".LongitudeStart", linesFC + ".LatitudeEnd", linesFC + ".LongitudeEnd", linesFC + ".Azimuth", lpiheaderFC + ".Direction", lpiheaderFC + ".LineLengthAmount", \
lpiheaderFC + ".SpacingIntervalAmount", lpiheaderFC + ".SpacingType"]
pdfOut.drawString(200,792,"Revist report for PlotID " + plotID)
with arcpy.da.SearchCursor("linesLayer", fields, sql_clause=(None, 'ORDER BY LineID')) as cursor:
for row in cursor:
# some plots have things like 1b in them
if len(row[0]) > 1:
lineNum = row[0][0]
else:
lineNum = row[0]
lineOffset = 760 - (220 * (int(lineNum) - 1))
pdfOut.drawString(20,lineOffset,"LineID: " + str(row[0]))
pdfOut.drawString(20,lineOffset - 20,"LatitudeStart: " + str(row[1]))
pdfOut.drawString(20,lineOffset - 40,"LongitudeStart: " + str(row[2]))
pdfOut.drawString(20,lineOffset - 60,"LatitudeEnd: " + str(row[3]))
pdfOut.drawString(20,lineOffset - 80,"LongitudeEnd: " + str(row[4]))
pdfOut.drawString(20,lineOffset - 100,"Azimuth: " + str(row[5]))
pdfOut.drawString(20,lineOffset - 120,"Direction: " + str(row[6]))
pdfOut.drawString(20,lineOffset - 140,"LineLengthAmount: " + str(row[7]))
pdfOut.drawString(20,lineOffset - 160,"SpacingIntervalAmount: " + str(int(row[8])))
pdfOut.drawString(20,lineOffset - 180,"SpacingType: " + str(row[9]))
arcpy.RemoveJoin_management("linesLayer")
arcpy.Delete_management("linesLayer")
# end page 2
pdfOut.showPage()
#----Page3
# Just get PrimaryKey records
whereClause = "PrimaryKey = '" + pk + "'"
arcpy.MakeTableView_management(soilhorSDE, "soilhorLayer", whereClause)
# Join SoilPitHorizons to SoilPits
arcpy.AddJoin_management("soilhorLayer", "SoilKey", soilpitsSDE, "SoilKey")
fields = [soilpitsFC + ".SoilDepthLower", soilpitsFC + ".DepthMeasure", soilpitsFC + ".Notes", soilhorFC + ".Texture", soilhorFC + ".RockFragments", \
soilhorFC + ".Effer", soilhorFC + ".HorizonDepthUpper", soilhorFC + ".HorizonDepthLower"]
pdfOut.drawString(200,792,"Revist report for PlotID " + plotID)
recCount = 1
# need to worry about dups so added PK whereclause and need to order the horizons so added order by
whereClause = soilpitsFC + ".PrimaryKey = '" + pk + "'"
with arcpy.da.SearchCursor("soilhorLayer", fields, whereClause, sql_clause=(None, 'ORDER BY HorizonDepthUpper')) as cursor:
for row in cursor:
lineOffset = 680 - (20 * (recCount - 1))
# Only need the Header info once
if recCount == 1:
if row[0]:
pdfOut.drawString(20,760,"SoilDepthLower: " + str(int(row[0])))
else:
pdfOut.drawString(20,760,"SoilDepthLower: None")
pdfOut.drawString(20,740,"DepthMeasure: " + row[1])
pdfOut.drawString(20,720,"Notes: " + str(row[2]))
pdfOut.drawString(20,700,"Soil Pit Horizons")
pdfOut.drawString(20,lineOffset," Depth Upper: " + str(int(row[6] or 0)).rjust(2,' ') + " Depth Lower: " + str(int(row[7] or 0)).rjust(2,' ') + " Texture: " + str(row[3]) + " Effer: " + str(row[5]) + " Rock Fragments: " + str(int(row[4] or 0)))
## pdfOut.drawString(20,lineOffset - 40,"LongitudeStart: " + str(row[2]))
## pdfOut.drawString(20,lineOffset - 60,"LatitudeEnd: " + str(row[3]))
## pdfOut.drawString(20,lineOffset - 80,"LongitudeEnd: " + str(row[4]))
## pdfOut.drawString(20,lineOffset - 100,"Azimuth: " + str(row[5]))
## pdfOut.drawString(20,lineOffset - 120,"Direction: " + row[6])
## pdfOut.drawString(20,lineOffset - 140,"LineLengthAmount: " + str(row[7]))
## pdfOut.drawString(20,lineOffset - 160,"SpacingIntervalAmount: " + str(row[8]))
## pdfOut.drawString(20,lineOffset - 180,"SpacingType: " + row[9])
recCount += 1
arcpy.RemoveJoin_management("soilhorLayer")
arcpy.Delete_management("soilhorLayer")
# end page 3
pdfOut.showPage()
#--Page4 Species List from Species Ind
pdfOut.drawString(200,792,"Revist report for PlotID " + plotID)
lineOffset = 760
fields = ["Species"]
whereClause = "PrimaryKey = '" + pk + "'"
speciesList = ""
# Get a list of species
with arcpy.da.SearchCursor(specindSDE, fields, whereClause) as cursor:
for row in cursor:
speciesList = speciesList + row[0] + ', '
# remove trailing , to be fancy
speciesList = speciesList[:-2]
pdfOut.drawString(20,lineOffset,"Species List: ")
# have to worry about long strings here
lineOffset = lineOffset-20
for line in textwrap.wrap(speciesList, 81):
pdfOut.drawString(30, lineOffset, str(line))
lineOffset -= 20
# end page 4
pdfOut.showPage()
#---Page5 and on
# Grad the images and add all of them. Easier to use file path than URL
photoPath = photosDir + "\\" + photoState + "\\" + photoKey
photoLinks = os.listdir(photoPath)
for photo in photoLinks:
print "Photo " + photo
if photo.endswith(".jpg") or photo.endswith(".JPG"):
#im = Image.open(photoPath + "\\" + photo)
#width, height = im.size
#pdfOut.drawImage(photoPath + "\\" + photo, width/4*inch, height/4*inch, width/2*inch, height/2*inch, mask='auto', preserveAspectRatio=True, anchor='c')
fill_page_with_image(photoPath + "\\" + photo, pdfOut)
# end the page
pdfOut.showPage()
# Save the pdf
pdfOut.save()
print "All Done"
Thanks
Code looks fine. I would suspect that the file formats between the drawings & pictures is different, and therefore returns NoneType. What format are the drawings in?
edit: looks like the ._getexif() method only supports jpg & tiff
I see that you added some conditional statements to the lines that were throwing the error. Use 'try' instead of if statements. That way, your script can handle errors w/o failing.
Mike