Skip to content
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

PDF header #40

Closed
michielkauwatjoe opened this issue Feb 5, 2016 · 25 comments
Closed

PDF header #40

michielkauwatjoe opened this issue Feb 5, 2016 · 25 comments

Comments

@michielkauwatjoe
Copy link

when importing DrawBot and generating a PDF, I'm getting an error after an update;

failed to find PDF header:%PDF' not found.`

as a warning, but second PDF won't build with this error:

It is advised to use 'size()' at the top of a script

Is this a new issue or am I doing something wrong?

@justvanrossum
Copy link
Collaborator

Not sure what you mean. Example code?

@michielkauwatjoe
Copy link
Author

Sorry, I'll try to explain: the following fails after first saveImage it seems, before it didn't have a problem with multiple runs.

import drawBot

drawBot.size('A3Landscape')
for ...
    drawBot.newDrawing()
    for ...
        drawBot.newPage(self.w, self.h)
        ... do stuff
    drawBot.saveImage(path-to-pdf)

@justvanrossum
Copy link
Collaborator

Still can't reproduce...

import drawBot

drawBot.size('A3Landscape')
for i in range(2):
    drawBot.newDrawing()
    for j in range(2):
        drawBot.newPage(500, 500)
        rect(10, 10, 200, 200)
    drawBot.saveImage("PDF-issue-40-%s.pdf" % i)

@justvanrossum
Copy link
Collaborator

But something has changed with size() and newPage() indeed: you can't call size() after you've already drawn things, or call size() twice. So maybe that's it?

@michielkauwatjoe
Copy link
Author

Thanks, the example works. Is there a way to reset the context or clear drawBot after saving the PDF? Size needs to be set once before I run my export routines but needs to be different from time to time...

@justvanrossum
Copy link
Collaborator

newDrawing() should do that. (In the example the first size() does nothing, as newDrawing() resets everything.)

I'm stil interested in your non-working example, as that PDF warning sounds suspicious.

@michielkauwatjoe
Copy link
Author

Right, so I probably switched newDrawing() and size(). Here's the relevant code I think:

import drawBot
import datetime
import traceback
from wayfinding.objects.destination import Destination
from wayfinding.objects.directionsign import DirectionSign
from wayfinding.objects.identificationsign import IdentificationSign
from wayfinding.objects.customsign import CustomSign
from wayfinding.mathandlogic import MathAndLogic

class Export(object):
    u"""
    Exports data to PDF.
    """
    ratio = None
    w = None
    h = None
    x0 = None
    y0 = None
    wImage = None
    wmm = None
    paperSize = None
    fonts = {'bold': 'CorpidC3-Bold', 'regular': 'CorpidC3-Regular'}

    def export(self, **kwargs):
        p = self.currentProject

        if kwargs['exportType'] == 'signContents':
            drawBot.size('A4')
            self.w = drawBot.width()
            self.h = drawBot.height()
            self.wmm = 210
            self.hmm = 297
            self.x0 = 55
            self.y0 = 100
            self.paperSize = 'A4'
        else:
            drawBot.size('A3Landscape')
            self.w = drawBot.width() # A3 width in pixels.
            self.h = drawBot.height()
            self.wmm = 420           # A3 width in millimeters.
            self.wImage = 950        # Blueprint width in pixels
            self.x0 = 55
            self.y0 = 100
            self.paperSize = 'A3'

            # Image ratio to scale down.
            self.ratio = float(self.wImage) / p.width

        self.checkFonts()

        if kwargs['exportType'] in ['currentView', 'signPlanOverview', 'destinationOverview']:
            self.exportDocument(**kwargs)
            ...

    def exportDocument(self, **kwargs):
        u"""
        Exports a single document.
        """
        p = self.currentProject
        floors = self.getExportFloors(p, kwargs['exportIsAllFloors'],
                                        kwargs['exportIsFloorRange'],
                                        kwargs['exportFloorRange'])

        drawBot.newDrawing()

        # Page for each floor.
        for floorNumber in floors:
            self.labelPositions = []
            floor = self.getFloor(floorNumber=floorNumber)
            exportObjects = self.getExportObjects(floor, kwargs['exportType'])
            MathAndLogic.setIntersections(floor)

            drawBot.newPage(self.w, self.h)
            self.exportBlueprint(p, floorNumber, kwargs['exportSize'])
            self.exportTitle(kwargs['exportType'])
            self.exportDestinations(floor, exportObjects['destinations'])
            self.exportDecisionAreas(floor, exportObjects['decisionAreas'])
            self.exportFlows(floor, exportObjects['flows'])
            self.exportSignGroups(floor, exportObjects['signGroups'])
            self.exportLabels(floor, exportObjects, kwargs['exportType'])
            self.exportMetadata(floor, exportIsConcept=kwargs['exportIsConcept'],
                                    exportType=kwargs['exportType'],
                                    exportComments=kwargs['exportComments'])
            self.exportScale(floor)
            self.exportCompass()
            self.exportFooter(floor, kwargs['exportIsConcept'])

        drawBot.saveImage("%s%s-%s.pdf" % (p.exportPath, kwargs['exportType'], self.joinFloors(floors)))

@justvanrossum
Copy link
Collaborator

If you could reduce that to a snippet we can just run, that would be great :)

@typemytype
Copy link
Owner

using size() in a multi page document is not advised.
size() actually exists for historical reasons

its advised to use:

newDrawing() # initiate a clear document

newPage(w, h) # add a page
# draw stuff

to know width, height of predefined sizes before you draw or setup a document use: sizes("A4") this returns a width, height tuple.

@michielkauwatjoe
Copy link
Author

I'm setting size after newDrawing and the second error (warning) has disappeared, still get the first one however. Might be a mistake on my side, I'll do some testing. If I stil yet the header error I'll send you a snippet. Also I'll start using newPage(), thanks!

@michielkauwatjoe
Copy link
Author

The PDF header error seems to occur when loading an image; tested with both png and tif files. Here's the example:

issue.py.zip

@justvanrossum
Copy link
Collaborator

Ok, I see the warning now. It also happens when just running in DrawBot, but the msg gets sent to the low level stderr, so it gets hidden there. The warning is not written by DrawBot but by the OS.

@justvanrossum
Copy link
Collaborator

Ah, I have a suspicion what causes it: when placing and image, DrawBot tests to see whether it may be PDF data by attempting to instantiate a PDFDocument. If that fails, it's not PDF. I'm pretty sure that causes the warning. Frederik: should we revert to checking the file extension?

@michielkauwatjoe
Copy link
Author

Yes, it seems to be an Objective C error that happens when opening a PDF. The funny thing is the output is fine.

@typemytype
Copy link
Owner

the isPDFcheck could also look for the ext and eliminate those directly

def isPDF(path):
    if os.path.splitext(path)[-1].lower() != ".pdf":
        return False
    url = AppKit.NSURL.fileURLWithPath_(path)
    return AppKit.PDFDocument.alloc().initWithURL_(url) is not None

@justvanrossum
Copy link
Collaborator

Or maybe (inspired by the actual issue here...):

def isPDF(path):
    return open(path).read(4) == "%PDF"

@typemytype
Copy link
Owner

yeah, what if the path is a URL?

gr Frederik
www.typemytype.com

On 10 Feb 2016, at 14:05, Just van Rossum notifications@github.com wrote:

Or maybe (inspired by the actual issue here...):

def isPDF(path):
return open(path).read(4) == "%PDF"

Reply to this email directly or view it on GitHub #40 (comment).

@typemytype
Copy link
Owner

NSData could be used

def isPDF(path):
    url = AppKit.NSURL.fileURLWithPath_(path)
    data = AppKit.NSData.dataWithContentsOfURL_(url)
    if data is None:
        return False
    if data.getBytes_range_(None, (0, 4)) != "%PDF":
        return False
    return AppKit.PDFDocument.alloc().initWithURL_(url) is not None

@justvanrossum
Copy link
Collaborator

Hm, but that's very wasteful if the file isn't a PDF, unless we can write it in a way that the data is only fetched once, regardless of image type. If that's hard: let's stick to file extensions.

@michielkauwatjoe
Copy link
Author

Getting a traceback when loading an image, seems to be related to this issue:

Traceback (most recent call last):
File "vanilla/vanillaBase.pyc", line 204, in action_
File "Callbacks.pyc", line 791, in exportOkayCallback
File "Export.pyc", line 43, in export
File "Export.pyc", line 116, in exportDocument
File "/Users/michiel/source/git/WayFinding/WayFindingApp/dist/WayFinding.app/Contents/Resources/lib/python2.7/drawBot/drawBotDrawingTools.py", line 293, in saveImage
self._drawInContext(context)
File "/Users/michiel/source/git/WayFinding/WayFindingApp/dist/WayFinding.app/Contents/Resources/lib/python2.7/drawBot/drawBotDrawingTools.py", line 100, in _drawInContext
attr(_args, *_kwargs)
File "/Users/michiel/source/git/WayFinding/WayFindingApp/dist/WayFinding.app/Contents/Resources/lib/python2.7/drawBot/context/baseContext.py", line 1556, in image
self._image(path, (x, y), alpha, pageNumber)
File "/Users/michiel/source/git/WayFinding/WayFindingApp/dist/WayFinding.app/Contents/Resources/lib/python2.7/drawBot/context/pdfContext.py", line 235, in _image
_isPDF, image = self._getImageSource(path, pageNumber)
File "/Users/michiel/source/git/WayFinding/WayFindingApp/dist/WayFinding.app/Contents/Resources/lib/python2.7/drawBot/context/pdfContext.py", line 213, in _getImageSource
_isPDF, _ = isPDF(url)
TypeError: 'bool' object is not iterable

@typemytype
Copy link
Owner

Strange I cannot find what I missed while adding the function isPDF and renaming the bools to _isPDF see 29cd60c

are you sure you have to the latest version? and reinstalled is as module?

@michielkauwatjoe
Copy link
Author

Yes it's updated to the latest version. I'm softlinking to the module from site-packages, could that be the problem?

@michielkauwatjoe
Copy link
Author

Sorry, I meant linking through a pth file, not a softlink.

@typemytype
Copy link
Owner

oh, my bad, should return a tuple

fixed e6fb858

should work in 3.77

thanks

@michielkauwatjoe
Copy link
Author

Great, drawing an image working again and the error is gone. Thanks for fixing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants