Personal tools

WebAPI/WebPrintAPI

From MozillaWiki

Jump to: navigation, search

Contents

First iteration: WebPrintAPI

Scope

This is about implementing a new API for printing on the web. It's a "low level" API that allows web developers to have more control over printing output.

Defining the PrintSetting API as used in PrintDocument.print(printSetting) is out of the scope of this document.

Notes

  • Use points as metric for printing (72 points per inch)
  • Use of "px" doesn't make sense. Therefore they are forbidden (e.g. in font-size="12px") - use "pt" instead.
  • Use of ctx.putImageData(...) and ctx.getImageData(...) doesn't make sense as there might not be something like "pixels" on the printer backend.
  • If the user selects File -> Print from the menu, the developer can skip the printing listening to the window.onBeforePrint, create a PrintDocument object and print this one. This allows "custom" page printing as you could expect it to be inside some apps (Google Docs etc).
  • There is only one page size per PrintDocument.
  • The size of the page is defined within the PrintDocument constructor. If the defined page size doesn't fit the printer's page size, the content get's aligned as specified by the 'scale' property (see below).
  • The rendering context used for printing (PrintRenderingContext) is very similar to the CanvasRenderingContext2D.

Open Questions

  • What is (0,0)? The top left corner of the physical page OR of the page taking into account the print margin?
  • Should there be different color spaces then RGB? There is a <a href="http://www.w3.org/TR/css3-gcpm/#cmyk-colors">proposal for CMYK colors in CSS</a>.
  • Should the browser display a progress view while the pages are drawn (this doesn't mean the actual printing output progress)?
  • Should there be additional onBeforePrint/onAfterPrint callbacks on the PrintDocument object, or are the window.onBeforePrint/onAfterPrint events enough?
  • This proposal assumes the print page size is passed during the PrintDocument constructor. A different way is to get the page size from the current selected printer's page size. Defining the page size once makes things easier but makes this API on the other side less powerful.
  • Should there be points2cm, points2inch, inches2points utility functions?
  • Lot more... TBD

Possible values for the 'scale' property (see below)

  • 'scale': Scales the drawing while keeping the ration to to fit on the print paper size
  • 'fit': Scales the drawing while not taking the ration of the printer paper size in account
  • 'center': No scaling, just placing the center of the drawing onto the center of the printer paper.

Example

 var pDoc = new PrintDocument(
                 'documentTitle',  // Title
                 'letter',         // Paper size
                 'scale',          // optional: see notes about "scale"
                 false             // optional: isLandscape?
               );
               
 // ---
 // Following functions MUST be defined by the developer.
 // ---
 
 // The number of pages to print.
 pDoc.calcPageCount = function(fontCtx) {
   var pageCount;
   
   // ..
   
   // The fontCtx is a very limited subset of the normal print ctx, that
   // is just about doing text measurements.
   fontCtx.font = '...';
   fontCtx.measureText(...);
   
   //...
   
   return pageCount; // 42
 };
 
 // Actually drawing function. Each page is drawn individually. This is
 // handy for page preview or if the user picks only a subset of the total
 // pages.
 pDoc.renderPage = function(pageNumber, ctx) {
   if (ctx.isPreview) {
     var dpi = ctx.dpi;
     
     // Only draw images in low resolution for preview
     // ...
   }
   
   // Render the page <pageNumber> to the ctx.
   ctx.draw...
   // ...
   
   // If the drawing takes very long, the developer might want to use
   // setTimeouts and continue the painting later one.
   if (printingWillTakeLong()) {
     setTimeout(function() {
       // Continue drawing...
       
       // This page is done drawing.
       ctx.endPage();
     })
   } else {
     ctx.endPage();
   }
 }
   
 // ---
 // Following functions/properties are implemented by the
 // WebPrintAPI. They are listed here only to get an idea what they
 // will return.
 // ---
 
 // Getter:
 pDoc.pageSize = {
   <widthInInch>,
   <heightInInch>,
 }
 
 // Getter:
 pDoc.unwriteableMargin = {
   top:    <inch>,
   bottom: <inch>,
   left:   <inch>,
   right:  <inch>
 }
 
 // Was the printing started but is not finished yet?
 pDoc.isPrinting;
 
 // Are all the pages drawn?
 pDoc.isFinished;
 
 // Start the printing progress. This opens the print dialog.
 // The passed in `printSetting` is not defined yet and is outside of the scope
 // of this proposal/API.
 pDoc.print(printSetting);
 
 // Stops the print process. Once all data is created, there is no way to stop
 // the printing anymore; that means, as soon as `pDoc.isFinished` is true,
 // calling this function has no effect.
 pDoc.abort();

Proposed API

See the example to get an idea. (This section is still work in progress.)

The PrintRenderingContext definition is very much the same as the HTML5 Canvas' Context

Changes are listed on top and are commented out. The reason for removing the APIs for the PrintRenderingContext is given on lines starting with //!.

 interface PrintFontContext: CanvasText
 {
   // back-reference to the printDocument.
   readonly attribute PrintDocument printDocument;
 }
 interface PrintRenderingContext {
   // ---
   // Stuff removed compared to CanvasRenderingContext2d.
   // ---
 
   //! There is no notion of "pixels" while printing. Therefore, remove all the
   //  APIs that deal with "raw" pixel data.
   // // pixel manipulation
   // ImageData createImageData(double sw, double sh);
   // ImageData createImageData(ImageData imagedata);
   // ImageData getImageData(double sx, double sy, double sw, double sh);
   // void putImageData(ImageData imagedata, double dx, double dy);
   // void putImageData(ImageData imagedata, double dx, double dy, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight);
 
   //! No sense to reference back to a canvas.
   // readonly attribute HTMLCanvasElement canvas;
 
   //! No focusRing drawing or scrolling.
   // void drawSystemFocusRing(Element element);
   // void drawSystemFocusRing(Path path, Element element);
   // boolean drawCustomFocusRing(Element element);
   // boolean drawCustomFocusRing(Path path, Element element);
   // void scrollPathIntoView();
   // void scrollPathIntoView(Path path);
 
   //! Remove the measureText function from this interface and add it to the
   // CanvasText interface to be reuseable for the PrintFontContext.
   // TextMetrics measureText(DOMString text);
 
   // ---
   // Stuff added compared to CanvasRenderingContext2d.
   // ---
   
   // back-reference to the printDocument.
   readonly attribute PrintDocument printDocument;
   
   // rendering context for preview?
   readonly attribute boolean isPreview;
   
   // dpi resolution. 0.0 = not defined.
   readonly attribute double dpi;
   
   // tell the backend that this page has finished rendering.
   void endPage();
     
   // ---
   // From here everything stays the same compared to CanvasRenderingContext2d.
   // ---
     
   // state
   void save(); // push state on state stack
   void restore(); // pop state stack and restore state
 
   // compositing
   attribute double globalAlpha; // (default 1.0)
   attribute DOMString globalCompositeOperation; // (default source-over)
 
   // colors and styles (see also the CanvasLineStyles interface)
   attribute any strokeStyle; // (default black)
   attribute any fillStyle; // (default black)
   CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
   CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
   CanvasPattern createPattern((HTMLImageElement or HTMLCanvasElement or HTMLVideoElement) image, DOMString repetition);
 
   // shadows
   attribute double shadowOffsetX; // (default 0)
   attribute double shadowOffsetY; // (default 0)
   attribute double shadowBlur; // (default 0)
   attribute DOMString shadowColor; // (default transparent black)
 
   // rects
   void clearRect(double x, double y, double w, double h);
   void fillRect(double x, double y, double w, double h);
   void strokeRect(double x, double y, double w, double h);
 
   // path API (see also CanvasPathMethods)
   void beginPath();
   void fill();
   void fill(Path path);
   void stroke();
   void stroke(Path path);
   void clip();
   void clip(Path path);
   boolean isPointInPath(double x, double y);
   boolean isPointInPath(Path path, double x, double y);
 
   // text (see also the CanvasText interface)
   void fillText(DOMString text, double x, double y, optional double maxWidth);
   void strokeText(DOMString text, double x, double y, optional double maxWidth);
 
   // drawing images
   void drawImage((HTMLImageElement or HTMLCanvasElement or HTMLVideoElement) image, double dx, double dy);
   void drawImage((HTMLImageElement or HTMLCanvasElement or HTMLVideoElement) image, double dx, double dy, double dw, double dh);
   void drawImage((HTMLImageElement or HTMLCanvasElement or HTMLVideoElement) image, double sx, double sy, double sw, double sh, double dx, double dy, double dw, double dh);
 };
 PrintRenderingContext implements CanvasTransformation;
 PrintRenderingContext implements CanvasLineStyles;
 PrintRenderingContext implements CanvasPathMethods;
 PrintRenderingContext implements CanvasText;
 
 [NoInterfaceObject]
 interface CanvasTransformation {
   // transformations (default transform is the identity matrix)
   void scale(double x, double y);
   void rotate(double angle);
   void translate(double x, double y);
   void transform(double a, double b, double c, double d, double e, double f);
   void setTransform(double a, double b, double c, double d, double e, double f);
 };
 
 [NoInterfaceObject]
 interface CanvasLineStyles {
   // line caps/joins
   attribute double lineWidth; // (default 1)
   attribute DOMString lineCap; // "butt", "round", "square" (default "butt")
   attribute DOMString lineJoin; // "round", "bevel", "miter" (default "miter")
   attribute double miterLimit; // (default 10)
 };
 
 [NoInterfaceObject]
 interface CanvasText {
   // text
   attribute DOMString font; // (default 10px sans-serif)
   attribute DOMString textAlign; // "start", "end", "left", "right", "center" (default: "start")
   attribute DOMString textBaseline; // "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" (default: "alphabetic")
   TextMetrics measureText(DOMString text);
 };
 
 [NoInterfaceObject]
 interface CanvasPathMethods {
   // shared path API methods
   void closePath();
   void moveTo(double x, double y);
   void lineTo(double x, double y);
   void quadraticCurveTo(double cpx, double cpy, double x, double y);
   void bezierCurveTo(double cp1x, double cp1y, double cp2x, double cp2y, double x, double y);
   void arcTo(double x1, double y1, double x2, double y2, double radius);
   void rect(double x, double y, double w, double h);
   void arc(double x, double y, double radius, double startAngle, double endAngle, optional boolean anticlockwise);
 };
 
 interface CanvasGradient {
   // opaque object
   void addColorStop(double offset, DOMString color);
 };
 
 interface CanvasPattern {
   // opaque object
 };
 
 interface TextMetrics {
   readonly attribute double width;
 };
 
 [Constructor(optional Element scope)]
 interface Path {
   void addFill(Path path);
   void addStroke(Path path);
   void addFillText(DOMString text, double x, double y, optional double maxWidth);
   void addStrokeText(DOMString text, double x, double y, optional double maxWidth);
   void addFillText(DOMString text, Path path, optional double maxWidth);
   void addStrokeText(DOMString text, Path path, optional double maxWidth);
 };
 Path implements CanvasTransformation;
 Path implements CanvasLineStyles;
 Path implements CanvasPathMethods;
 Path implements CanvasText;

Implementation

Some basic investigation in the Gecko codebase. No code written so far.