{"action":"created","issue":{"url":"https://api.github.com/repos/qpdf/qpdf/issues/341","repository_url":"https://api.github.com/repos/qpdf/qpdf","labels_url":"https://api.github.com/repos/qpdf/qpdf/issues/341/labels{/name}","comments_url":"https://api.github.com/repos/qpdf/qpdf/issues/341/comments","events_url":"https://api.github.com/repos/qpdf/qpdf/issues/341/events","html_url":"https://github.com/qpdf/qpdf/issues/341","id":463339654,"node_id":"MDU6SXNzdWU0NjMzMzk2NTQ=","number":341,"title":"Handle \"xref\" more tolerant on broken PDF files.","user":{"login":"ams-tschoening","id":6223655,"node_id":"MDQ6VXNlcjYyMjM2NTU=","avatar_url":"https://avatars1.githubusercontent.com/u/6223655?v=4","gravatar_id":"","url":"https://api.github.com/users/ams-tschoening","html_url":"https://github.com/ams-tschoening","followers_url":"https://api.github.com/users/ams-tschoening/followers","following_url":"https://api.github.com/users/ams-tschoening/following{/other_user}","gists_url":"https://api.github.com/users/ams-tschoening/gists{/gist_id}","starred_url":"https://api.github.com/users/ams-tschoening/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ams-tschoening/subscriptions","organizations_url":"https://api.github.com/users/ams-tschoening/orgs","repos_url":"https://api.github.com/users/ams-tschoening/repos","events_url":"https://api.github.com/users/ams-tschoening/events{/privacy}","received_events_url":"https://api.github.com/users/ams-tschoening/received_events","type":"User","site_admin":false},"labels":[{"id":440258274,"node_id":"MDU6TGFiZWw0NDAyNTgyNzQ=","url":"https://api.github.com/repos/qpdf/qpdf/labels/next","name":"next","color":"0e8a16","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2019-07-02T17:04:07Z","updated_at":"2019-07-03T08:05:07Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"One of my customers provided me some PDF file which QPDF is unable to parse properly and I'm somewhat sure that the problem is with the PDF itself, but in theory QPDF could handle that specific problem more tolerant. As it's doing so for other cases with non-standard `xref` already, I would like to document my problem her and discuss possible solutions. While I'm not able to share the whole PDF for privacy reasons, I'm going to share the most relevant parts according to my tests.\r\n\r\nThe following is the error:\r\n\r\n    C:\\Users\\tschoening\\Desktop>qpdf --check \"Test.pdf\"\r\n    WARNING: Test.pdf: file is damaged\r\n    WARNING: Test.pdf (offset 31352): xref not found\r\n    WARNING: Test.pdf: Attempting to reconstruct cross-reference table\r\n    Test.pdf: unable to find trailer dictionary while recovering damaged file\r\n\r\nThe following is the relevant part around `xref`:\r\n\r\n\t/Creator(Neevia docuPrinter TSE v6.8)\r\n\t/Author(s0330765)>>endobj\r\n\txref\r\n\t0 28\r\n\t0000000000 65535 f \r\n\t[...]\r\n\t0000029431 00000 n \r\n\ttrailer\r\n\t<< /Size 28 /Root 1 0 R /Info 2 0 R\r\n\t/ID [<FF3A5C433DA07654221870C2893843C3><FF3A5C433DA07654221870C2893843C3>]\r\n\t>>\r\n\tstartxref\r\n\t31352\r\n\t%%EOF\r\n\r\nAccording to my tests, `startxref` simply is off by one: 31352 is the newline BEFORE `xref`, like can be seen in the following screenshot. Increasing that value to 31353 using some HEX editor makes the problem go away and allows QPDF to successfully parse the PDF.\r\n\r\n![Clipboard01](https://user-images.githubusercontent.com/6223655/60530467-15520f80-9cf9-11e9-96fa-76ac102c5bc4.png)\r\n\r\nLooking at [QPDF::read_xref](https://github.com/qpdf/qpdf/blob/master/libqpdf/QPDF.cc#L580) I saw that you are already dealing with `xref`s violating the official spec and hoped you would provide a workaround for my problem as well:\r\n\r\n> // The PDF spec says xref must be followed by a line\r\n> // terminator, but files exist in the wild where it is\r\n>  // terminated by arbitrary whitespace.\r\n\r\nI'm of course willing to provide a PR, but can think of multiple possible solutions and would like to first check with you for the best approach: One approach could be changing `read_xref` to additionally check for the problem I have. Might get clumsy and the caller doesn't get a correct offset this way.\r\n\r\nAnother option might be changing [QPDF::parse](https://github.com/qpdf/qpdf/blob/master/libqpdf/QPDF.cc#L403), where `read_xref` gets actually called and add another call to that function with an increased offset by one in the exception handler. Or one could add some additional `read_xref` optionally with an additional signature or such doing exactly that by catching the exception, recalling with an increased offset and in case of success return the correct offset to the caller.\r\n\r\nIn [QPDF::parse](https://github.com/qpdf/qpdf/blob/master/libqpdf/QPDF.cc#L391) again, where the actual offset is read, one could add some logic to check that the offset really belongs to some `x` of `xref` and else might increase it or such.\r\n\r\nI would prefer the option internally wrapping `read_xref` to handle additional strategies of fault-tolerance, maybe giving it some other name: `read_xref_tolerant` or such. In that wrapper one could try again using my +1, maybe a +2 is needed at some point, maybe the space-handling after `xref` could be externalized as well etc.\r\n\r\nWhat do you think?"},"comment":{"url":"https://api.github.com/repos/qpdf/qpdf/issues/comments/507986888","html_url":"https://github.com/qpdf/qpdf/issues/341#issuecomment-507986888","issue_url":"https://api.github.com/repos/qpdf/qpdf/issues/341","id":507986888,"node_id":"MDEyOklzc3VlQ29tbWVudDUwNzk4Njg4OA==","user":{"login":"ams-tschoening","id":6223655,"node_id":"MDQ6VXNlcjYyMjM2NTU=","avatar_url":"https://avatars1.githubusercontent.com/u/6223655?v=4","gravatar_id":"","url":"https://api.github.com/users/ams-tschoening","html_url":"https://github.com/ams-tschoening","followers_url":"https://api.github.com/users/ams-tschoening/followers","following_url":"https://api.github.com/users/ams-tschoening/following{/other_user}","gists_url":"https://api.github.com/users/ams-tschoening/gists{/gist_id}","starred_url":"https://api.github.com/users/ams-tschoening/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ams-tschoening/subscriptions","organizations_url":"https://api.github.com/users/ams-tschoening/orgs","repos_url":"https://api.github.com/users/ams-tschoening/repos","events_url":"https://api.github.com/users/ams-tschoening/events{/privacy}","received_events_url":"https://api.github.com/users/ams-tschoening/received_events","type":"User","site_admin":false},"created_at":"2019-07-03T08:05:07Z","updated_at":"2019-07-03T08:05:07Z","author_association":"CONTRIBUTOR","body":"> I appreciate the offer of a PR, but my guess is that it will take me less time to look at the code and fix it myself than to analyze a PR and decide whether it's a complete and correct fix.\r\n\r\nDo you have any time schedule? While I don't want to apply any pressure of course, I have the problem that my customer might run into this problem at any time again and at least next time is simply going to ask me for when this will be fixed. :-) I would like to avoid applying a custom patch then unlikely to get merged as PR, just to need to remove that patch when you are fixing it differently.\r\n\r\nAdditionally I wrote to the company creating that PDF writer and asked for a fix on their site as well, just to be sure."}}
{"actor":{"display_login":"ams-tschoening"}}