When using the Draw appearance it seems the orientation of the image is not set

218
3
02-01-2021 10:38 AM
DougBrowning
MVP Notable Contributor

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

0 Kudos
3 Replies
by Anonymous User
Not applicable

Does it work with all pictures, or just a few? Can you post the rest of your code?

0 Kudos
DougBrowning
MVP Notable Contributor

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

 

0 Kudos
by Anonymous User
Not applicable

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

0 Kudos