5 ms·
> Acrobat's "Distillery" Minor nit, it was called Distiller. I worked on it for about a year, not so much on the core processing code, but on the periphery - u
by Stratoscope 3y ago
> Acrobat's "Distillery"
Minor nit, it was called Distiller. I worked on it for about a year, not so much on the core processing code, but on the periphery - user interface, adding an API, etc.
When I first started work on it, one of my new colleagues walked by and asked what I'd be working on. I said "Distiller", and they gave me a sad look, like "you just got the worst assignment here."
> PDF is like PostScript without the programming language, just the imaging model.
Of course PDF does have a programming language, a very old version of JavaScript, with an API that Acrobat/Reader provides.
It's mostly used for simple stuff like calculating and updating one form field from your input in another.
You can tell when you look at the API that it was designed 20-25 years ago.
> (But with a lot of extra crap thrown in so haphazardly and indiscriminately that the Adobe PDF Viewer browser plugin traditionally requires downloading patches almost every day!)
Browsers don't use the Adobe plugin any more, thank goodness.
But on the "haphazard and indiscriminate" part, yes, guilty as charged.
One of my later projects at Adobe was to build a multimedia plugin that could embed any of the popular media players of the day: Real Player, QuickTime, Windows Media Player, one or two others I don't remember, and even Flash.
I built the main plugin which provided a JavaScript API you could access from your PDF and a way to add a "player plugin" for each media player, and teamed up with colleagues to build each of the individual player plugins.
No one at Adobe around 2002 thought Flash was important, so I kind of had to do a stealth minimal implementation.
It was definitely a fun project, and also a great example of the "haphazard and indiscriminate" additions you referred to.
- DonHopkins 3y ago"Distillery" was actually the name of the original PostScript version that Glenn Reid posted to usenet in the 1980's. Here is a copy of his "release 10 of the Distillery" from 10 Mar 1989: https://donhopkins.com/home/archive/postscript/newerstill.ps.txt https://donhopkins.com/home/archive/postscript/newerstill.ps... It was actually John Warnock's original idea, which I learned about from Owen Densmore's paper about using NeWS as a "linguistic motherboard": https://news.ycombinator.com/item?id=28115946 https://news.ycombinator.com/item?id=28115946 >Glenn Reid wrote a PostScript partial evaluator in PostScript that optimized other PostScript drawning programs, called "The Distillery". You would send still.ps to your PostScript printer, and then send another PostScript file that drew something to the printer. The first PostScript Distillery program would then partially evaluate the second PostScript drawing program, and send back a third PostScript program, an optimized drawing program, with all the loops and conditionals unrolled, calculations and transformations pre-computed, all in the same coordinate system. >It was originally John Warnock's idea, that Glenn implemented. And it led to Adobe Acrobat's "Distiller". Acrobat is basically PostScript without the programming language. [...] https://news.ycombinator.com/item?id=33827923 https://news.ycombinator.com/item?id=33827923 >Owen Densmore recounted John Warnock's idea that PostScript was actually a "linguistic motherboard". (This was part of a discussion with Owen about NeFS, which was a proposal for the next version of NFS to run a PostScript interpreter in the kernel. More about that here:) https://news.ycombinator.com/item?id=30558915 https://news.ycombinator.com/item?id=30558915 >Glenn Reid described his classic "Grandfather Clock" metaphor to comp.lang.postscript that really opened my eyes about writing code in interpreted languages like PostScript (and JavaScript, while JIT'ed at runtime, still makes calling native code expensive), and balancing optimization with readability. With modern JavaScript/canvas, JavaScript code that calls other JavaScript code runs really fast, but calling between JavaScript and built-in code is still slow, so it's good to have the built-in code do as much as possible when you do (like rendering a long string of text at once, instead of rendering it word by word like many PostScript drivers would): >Glenn's post in a comp.lang.postscript discussion about PostScript programming style and optimization: https://groups.google.com/forum/#!search/%22glenn$20reid%22$20%22grandfather$20clock%22/comp.lang.postscript/i4NcunJGFkU/Boxz_6s-T3cJ https://groups.google.com/forum/#!search/%22glenn$20reid%22$... From: Glenn Reid (Abode Systems) Newsgroup: comp.lang.postscript Subject: Re: An Idea to Help Make Postscript Easier to Read (and Write) Date: 10 Sep 88 17:26:24 GMT You people tend to forget that the PostScript language is interpreted. It is well and good to use tools to convert to and from PostScript, but it is not quite as "transparent" as we all might think. I like to think of a big grandfather clock, with the pendulum swinging. Each time pendulum swings, the PostScript interpreter gets to do one operation. The "granularity" of the clock is nowhere near the speed of a microprocessor instruction set, and any comparison with assembly languages doesn't make sense. The difference between: 0 0 moveto and 0 0 /arg2 exch def /arg1 exch def arg1 arg2 moveto can sort of be measured in "ticks" of the interpreter's clock. It's not quite this simple, since simply pushing a literal is faster than executing a real PostScript operator, but it is a rough rule of thumb. It will take about three times as long to execute the second of these in a tight loop, and about five times as long if it is transmitted and scanned each time. My rule of thumb is that if you have roughly the same number of tokens in your stack approach as you do with your 'exch def' approach, the 'exch def' is likely to be much more readable and better. Otherwise, I usually go with the stack approach. One other thing of note is that if you have too much stack manipulation going on, it may well be symptomatic of a problem in the original program design. Also, most procedures don't do any stack manipulation at all, they simply use their arguments directly from the stack. In this situation, it is especially wasteful (and confusing, I think) to declare intermediate variables. Compare: % sample procedure call: (Text) 100 100 12 /Times-Roman SETTEXT % approach 1: /SETTEXT { %def findfont exch scalefont setfont moveto show } def % approach 2: /SETTEXT { %def /arg5 exch def /arg4 exch def /arg3 exch def /arg2 exch def /arg1 exch def arg5 findfont arg4 scalefont setfont arg2 arg3 moveto arg1 show } def Which of these is easier for you to understand? Anyway, I think the discussion is a good one, but let's not forget that PostScript it is an interpreted language. And I don't think it is terribly hard to use and understand, if it is written well. Glenn Reid Adobe Systems
- Stratoscope 3y ago> "Distillery" was actually the name of the original PostScript version that Glenn Reid posted to usenet in the 1980's. Of course! I would never dispute that, and thank you for all the interesting historical details. I was only saying that when I worked on it some 10 years later, the "y" had disappeared from the name, and it was called Acrobat Distiller. https://helpx.adobe.com/acrobat/using/creating-pdfs-acrobat-distiller.html https://helpx.adobe.com/acrobat/using/creating-pdfs-acrobat-...
- kragen 3y agoi've been fiddling with a series of prototype interpreters for a simple stack machine over the last year or so, and i've come to a few tentative conclusions: 1. it's probably a win to implicitly stick the arguments to a subroutine into local variables on subroutine entry, as smalltalk-80 and elisp and java do, rather than letting the subroutine do that itself if it wants to, as forth and postscript and hp calculators do. this is mostly for convenience and readability, but it also affects efficiency and safety. in the case of a straightforward stack interpreter, each stack operation takes about the time of 10 machine instructions. for example, in http://canonical.org/~kragen/sw/dev3/hadixmv.S http://canonical.org/~kragen/sw/dev3/hadixmv.S, the addition bytecode instruction is implemented by the following arm assembly code ldmdb r4!, {r3} @ cargar segundo item de la pila r4 en r3 temporario add r5, r5, r3 @ sumar número cargado al primero en pila dispatch here `dispatch` is an assembly macro that expands (in my un-checked-in version) to ldrb r3, [r6], #1 @ cargar próxima instrucción, post-index ldr pc, [r7, r3, lsl #2] @ indexar tabla de bytecodes apuntada por r7 so this is four instructions, but three of them access memory, making them slower (on my target platform), and the last one usually causes a pipeline bubble, so this takes typically 10 clocks on my target chip a few bytecode instructions are a bit faster than this, a few are much slower, and the average seems to come out to about 15 clocks, while an average instruction is maybe closer to 1.2 clocks, depending on your code. so there's a strong temptation to do anything that will allow you to waste less time in bytecode dispatch i think glenn is strawmanning a bit here with his arg5. i think you'd write something like /settext [/text /x /y /size /font] { font findfont size scalefont setfont x y moveto text show } fn that's still 10 calls inside the subroutine instead of 6, but glenn only got down to 6 because he put the arguments in almost the most convenient order 2. probably a super dumb jit compiler, if well optimized, will never be much slower than a straightforward bytecode interpreter, and in common cases will be about three times faster, but the overall system performance will still be about three times slower than well-optimized assembly. and i can write the compiler in the language i'm implementing (though i can't optimize it that way) which is a lot more convenient than writing it in assembly. even arm assembly, which is super sweet. for this reason, for many years now, the jvm 'interpreter' (what it uses for interpretation of everything that hasn't yet been found to be a hotspot) is a super dumb jit compiler and not actually an interpreter this tends to undermine the performance argument for automatically creating local variables like this, but i think not completely 3. stack machines have more compact code than register machines, but because you need about twice as many operations to do a given job, the interpreter dispatch overhead is twice as expensive. my primary objective is code density, and although i haven't compiled anything big yet, i think i can get about 2/3 the code size of thumb-2 or rv32c. but a register-based code is probably better when code density isn't the primary objective