1. 소개
Web Printing API는 격리된 컨텍스트에서 인쇄 기능을 구현하는 데 전례 없는 유연성을 제공합니다. 이 API는 일반 웹 컨텍스트에는 노출되지 않습니다.
1.1. 기존 대안
존재하는
window.
메서드가 있지만, 기본적으로
인쇄 대화 상자를 열고 나머지는 사용자가 하도록 요청합니다. Web Printing API는
앱 개발자가 격리된 컨텍스트의 웹 애플리케이션에서
운영체제에서 로컬로 사용할 수 있는 프린터에 직접 접근할 수 있게 해 주며,
사용자 지정 인쇄 속성(용지 크기, 색상 설정, 품질 등)으로
인쇄 작업을 제출할 수 있게 합니다.
print()
1.2. Web Printing API 기능
Web Printing API를 사용하면 다음을 할 수 있습니다.
-
대화 상자 없이 코드에서 바로 인쇄 작업을 제출할 수 있습니다!
-
자체 인쇄 대화 상자를 구현할 수 있습니다!
-
사용자 상호작용 없이 사용자 지정 인쇄 속성으로 인쇄할 수 있습니다!
-
프린터를 나열하고 프린터 기능뿐 아니라 프린터 상태(유휴, 사용 중 등)를 가져올 수 있습니다. 프린터 기능을 알고 있으면 앱은 특정 인쇄 속성을 가진 인쇄 준비 완료 파일을 준비할 수 있습니다.
-
어떤 방식으로든 인쇄를 자동화할 수 있습니다. 이전에 반복되던 인쇄 워크플로를 구축할 수 있습니다.
-
그뿐만 아니라,
window.와 달리, 인쇄 작업의 작업 상태, 즉 완료되었는지, 실패했는지 등을 관찰할 수 있습니다.print()
RFC8011의 섹션 5는 더 자세히 알아보는 데 특히 유용합니다 (https://datatracker.ietf.org/doc/html/rfc8011#section-5).
2. 예제
2.1. 프린터 및 기본 속성 나열
try { const printers= await printing. getPrinters(); for ( const printerof printers) { const attributes= printer. cachedAttributes(); console. log( $attributes { printerName . has the following } basic ( attributes ) $ : attributes { } ); } } catch ( err) { console. warn( "Printing operation failed: " + err); }
2.2. 프린터 및 자세한 속성 나열
try { const printers= await printing. getPrinters(); const promises= printers. map( printer=> printer. fetchAttributes()); Promise. all( promises). then(( values) => { for ( const attributesof values) { console. log( $attributes { printerName . has the following } detailed ( attributes ) $ : attributes { } ); } }); } catch ( err) { console. warn( "Printing operation failed: " + err); }
2.3. 프린터 상태 쿼리
try { const printers= await printing. getPrinters(); const printer= printers. find( printer=> printer. cachedAttributes(). printerName=== 'Brother QL-820NWB' ); const attributes= await printer. fetchAttributes(); console. log( $attributes { printerName . 's } state is $ new attributes { printerState . } ! ); } catch ( err) { console. warn( "Printing operation failed: " + err); }
2.4. 인쇄 작업 제출
try { const printers= await printing. getPrinters(); const printer= printers. find( printer=> printer. cachedAttributes(). printerName=== 'Brother QL-820NWB' ); const printJob= await printer. submitPrintJob( "Sample Print Job" , new Blob(...), { copies: 2 , media: 'iso_a4_210x297mm' , multipleDocumentHandling: 'separate-documents-collated-copies' , printerResolution: { crossFeedDirectionResolution: 300 , feedDirectionResolution: 400 , units: 'dots-per-inch' }, sides: 'one-sided' , printQuality: 'high' , pageRanges: [{ from : 1 , to: 5 }, { from : 7 , to: 10 }], }); const printJobComplete= new Promise(( resolve, reject) => { printJob. onjobstatechange= () => { const jobState= printJob. attributes(). jobState; if ( IsErrorStatus( jobState)) { console. warn( Job errored$ : jobState { } ); reject( /**/ ); return ; } if ( jobState=== "completed" ) { console. log( "Job complete!" ); resolve( /**/ ); return ; } console. log( Job state changed to $jobState { } ); }; }); await printJobComplete; } catch ( err) { console. warn( "Printing operation failed: " + err); }
2.5. 인쇄 작업 취소
try { const printers= await printing. getPrinters(); const printer= printers. find( printer=> printer. cachedAttributes(). printerName=== 'Brother QL-820NWB' ); const printJob= await printer. submitPrintJob(...); // This might take no effect if the job has already finished. printJob. cancel(); } catch ( err) { console. warn( "Printing operation failed: " + err); }
3. Window
인터페이스에 대한 확장
[Exposed =Window ,SecureContext ,IsolatedContext ]partial interface Window { [SameObject ]readonly attribute WebPrintingManager ; };printing
각 Window
객체는 고유한 WebPrintingManager
객체 인스턴스와 연결되며, 이는 Window
객체가 생성될 때 할당됩니다.
4.
WebPrintingManager
[Exposed =Window ,SecureContext ,IsolatedContext ]interface {WebPrintingManager Promise <sequence <WebPrinter >>(); };getPrinters
이 인터페이스의 메서드는 일부 단계를 병렬로 실행하며, web printing task source를 통해 메인 스레드에 작업을 다시 큐에 넣습니다.
getPrinters()
메서드 단계는 다음과 같습니다.
-
this의 관련 전역 객체의 연결된 Document가 "web-printing"이라는 정책 제어 기능을 사용하도록 허용되지 않은 경우, "
NotAllowedError"DOMException을 throw합니다. -
promise를 새 promise로 둡니다.
-
다음 단계를 병렬로 실행합니다.
-
local_printers를 운영체제에서 로컬로 사용할 수 있는 모든 프린터로 둡니다.
-
attributes_list를
WebPrinterAttributes딕셔너리의 빈 목록으로 둡니다. -
local_printers의 각 printer에 대해 반복합니다.
-
web_printer_attributes를 새
WebPrinterAttributes딕셔너리로 둡니다. -
web_printer_attributes의
printerName을 printer의 이름으로 설정합니다. -
web_printer_attributes의
printerId를 printer의 id로 설정합니다. 이 id는 자기 자신의 SHA256 해시를 16진수 문자열 표현으로 반환하여 난독화되어야 합니다. -
web_printer_attributes를 attributes_list에 추가합니다.
-
-
global에서 전역 작업을 큐에 넣어 web printing task source를 사용해 다음 단계를 실행합니다.
-
web_printers를
WebPrinter객체의 빈 목록으로 둡니다. -
attributes_list의 각 attributes에 대해 반복합니다.
-
web_printer를 attributes가 attributes로 설정된 새
WebPrinter로 둡니다. -
web_printer를 web_printers에 추가합니다.
-
-
promise를 web_printers로 resolve합니다.
-
-
-
promise를 반환합니다.
5. WebPrinter
[Exposed =Window ,SecureContext ,IsolatedContext ]interface {WebPrinter WebPrinterAttributes ();cachedAttributes Promise <WebPrinterAttributes >();fetchAttributes Promise <WebPrintJob >(submitPrintJob USVString ,job_name Blob ,document_data optional WebPrintJobTemplateAttributes = {}); };template_attributes
각 WebPrinter에는
attributes가 있으며, 이는
WebPrinterAttributes의
인스턴스입니다.
처음에는
printerName
및 printerId만
포함하며, 더 많은 정보를 얻으려면 fetchAttributes()를
사용해야 합니다.
cachedAttributes()
메서드는 attributes를 반환합니다.
fetchAttributes()
메서드 단계는 다음과 같습니다.
-
promise를 새 promise로 둡니다.
-
다음 단계를 병렬로 실행합니다.
-
프린터와 통신하는 동안 문제가 있는 경우, promise를 새
NetworkErrorDOMException으로 reject하고 이 단계를 중단합니다. -
new_web_printer_attributes를
WebPrinterAttributes의 새 인스턴스로 둡니다. -
프린터에 프린터 기능을 쿼리합니다. 반환된 기능의 목록을 printer_capabilities로 둡니다.
-
printer_capabilities의 각 printer_capability에 대해 반복합니다.
-
Internet Printing Protocol(RFC 8011)에 따라 printer_capability가
WebPrinterAttributes딕셔너리에 부합하도록 필요한 매핑을 수행합니다. -
매핑된 printer_capability로 new_web_printer_attributes의 해당 필드를 설정합니다(예: media source와 관련된 printer_capability는
mediaSourceDefault및mediaSourceSupported의 유효한 값으로 매핑되어야 합니다). -
프린터에 프린터 상태를 쿼리합니다. new_web_printer_attributes의
printerState를 반환된 프린터 상태 값으로 설정합니다. -
attributes를 new_web_printer_attributes로 설정합니다.
-
promise를 new_web_printer_attributes로 resolve합니다.
-
-
promise를 반환합니다.
submitPrintJob()
메서드 단계는 다음과 같습니다.
-
promise를 새 promise로 둡니다.
-
다음 단계를 병렬로 실행합니다.
-
프린터와 통신하는 동안 문제가 있는 경우, promise를 새
NetworkErrorDOMException으로 reject하고 이 단계를 중단합니다. -
fetchAttributes()의 알고리즘을 사용하여 attributes를 업데이트합니다. -
printer를
submitPrintJob()가 실행되는WebPrinter인스턴스로 둡니다. -
template_attributes의 각 template_attribute에 대해 반복합니다. -
template_attribute가 해당 printer의 attributes에 대해 확인한 지원 값(예:
mediaSource를mediaSourceSupported에 대해 확인한 값)을 포함하지 않는 경우, promise를 새DataErrorDOMException으로 reject하고 이 단계를 중단합니다. -
pdf_data가 PDF 문서 데이터를 보유하도록 합니다.
document_dataBlob을 PDF 문서로 변환하고 그 값을 pdf_data로 설정합니다.document_data가 잘못된 형식, 즉 유효한 PDF 문서가 아닌 경우, promise를 새DataErrorDOMException으로 reject하고 이 단계를 중단합니다. -
pdf_data를
WebPrintJobTemplateAttributes와 함께 보내고 프린터에 인쇄 작업으로 제출합니다. -
print_job를
WebPrintJob인터페이스의 인스턴스로 둡니다. print_job을 방금 프린터에 제출한 인쇄 작업과 연결합니다.
-
-
promise를 print_job으로 resolve합니다.
-
promise를 반환합니다.
6. WebPrintJob
[Exposed =Window ,SecureContext ,IsolatedContext ]interface :WebPrintJob EventTarget {WebPrintJobAttributes ();attributes undefined ();cancel attribute EventHandler ; };onjobstatechange
각 WebPrintJob에는
attributes가 있으며, 이는
WebPrintJobAttributes의
인스턴스이고, 처음에는 비어 있습니다.
attributes()
메서드는 인쇄 작업이 어떤 상태에 있는지(예: 완료된 페이지 수)를 보여 주는
attributes를
반환합니다.
cancel()
메서드는 인쇄 작업을 즉시 중단합니다.
signal
매개변수로 전달된 AbortSignal
타입을 사용해 수행할 수 있으며, 이는
submitPrintJob()
메서드를 호출할 때 WebPrintJobTemplateAttributes의
일부로 전달됩니다.
onjobstatechange는
onjobstatechange
이벤트 타입에 대한 이벤트 핸들러 IDL 속성입니다.
사용자
에이전트는 인쇄 작업의
WebPrintJobState
또는 jobPagesCompleted가
변경될 때마다 onjobstatechange
이벤트를 반드시 발생시켜야 합니다.
7. 데이터 모델
WebPrinterAttributes
- attributes를
나타내는 딕셔너리입니다.
WebPrintJobTemplateAttributes
- 인쇄 작업 속성을 나타내는 딕셔너리입니다.
dictionary {WebPrinterAttributes USVString ;printerName USVString ;printerId unsigned long ;copiesDefault WebPrintingRange ;copiesSupported WebPrintingMediaCollection ;mediaColDefault sequence <WebPrintingMediaCollection >;mediaColDatabase USVString ;mediaSourceDefault sequence <USVString >;mediaSourceSupported WebPrintingMimeMediaType ;documentFormatDefault sequence <WebPrintingMimeMediaType >;documentFormatSupported WebPrintingMultipleDocumentHandling ;multipleDocumentHandlingDefault sequence <WebPrintingMultipleDocumentHandling >;multipleDocumentHandlingSupported WebPrintingOrientationRequested ;orientationRequestedDefault sequence <WebPrintingOrientationRequested >;orientationRequestedSupported WebPrintingResolution ;printerResolutionDefault sequence <WebPrintingResolution >;printerResolutionSupported WebPrintColorMode ;printColorModeDefault sequence <WebPrintColorMode >;printColorModeSupported WebPrinterState ;printerState USVString ;printerStateMessage sequence <WebPrinterStateReason >;printerStateReasons WebPrintQuality ;printQualityDefault sequence <WebPrintQuality >;printQualitySupported WebPrintingSides ;sidesDefault sequence <WebPrintingSides >; };sidesSupported dictionary {WebPrintJobTemplateAttributes unsigned long ;copies WebPrintingMediaCollectionRequested ;mediaCol USVString ;mediaSource WebPrintingMultipleDocumentHandling ;multipleDocumentHandling WebPrintingOrientationRequested ;orientationRequested WebPrintingResolution ;printerResolution WebPrintColorMode ;printColorMode WebPrintQuality ;printQuality WebPrintingSides ;sides AbortSignal ; };signal dictionary {WebPrintingRange unsigned long ;from unsigned long ; };to dictionary {WebPrintingResolution unsigned long ;crossFeedDirectionResolution unsigned long ;feedDirectionResolution WebPrintingResolutionUnits ; };units typedef (WebPrintingRange or unsigned long );WebPrintingMediaSizeDimension dictionary {WebPrintingMediaSize WebPrintingMediaSizeDimension ;yDimension WebPrintingMediaSizeDimension ; };xDimension dictionary {WebPrintingMediaCollection USVString ;mediaSizeName WebPrintingMediaSize ; };mediaSize dictionary {WebPrintingMediaSizeRequested required unsigned long ;yDimension required unsigned long ; };xDimension dictionary {WebPrintingMediaCollectionRequested required WebPrintingMediaSizeRequested ; };mediaSize dictionary {WebPrintJobAttributes USVString ;jobName unsigned long ;jobPages unsigned long ;jobPagesCompleted WebPrintJobState ; };jobState enum {WebPrintingMimeMediaType , };"application/pdf" enum {WebPrintingMultipleDocumentHandling ,"separate-documents-collated-copies" , };"separate-documents-uncollated-copies" enum {WebPrintingOrientationRequested ,"portrait" , };"landscape" enum {WebPrintingResolutionUnits ,"dots-per-inch" , };"dots-per-centimeter" enum {WebPrintingSides ,"one-sided" ,"two-sided-long-edge" , };"two-sided-short-edge" enum {WebPrintQuality ,"draft" ,"normal" , };"high" enum {WebPrintColorMode ,"color" , };"monochrome" enum {WebPrinterState ,"idle" ,"processing" , };"stopped" enum {WebPrinterStateReason ,"none" ,"other" ,"connecting-to-device" ,"cover-open" ,"developer-empty" ,"developer-low" ,"door-open" ,"fuser-over-temp" ,"fuser-under-temp" ,"input-tray-missing" ,"interlock-open" ,"interpreter-resource-unavailable" ,"marker-supply-empty" ,"marker-supply-low" ,"marker-waste-almost-full" ,"marker-waste-full" ,"media-empty" ,"media-jam" ,"media-low" ,"media-needed" ,"moving-to-paused" ,"opc-life-over" ,"opc-near-eol" ,"output-area-almost-full" ,"output-area-full" ,"output-tray-missing" ,"paused" ,"shutdown" ,"spool-area-full" ,"stopped-partly" ,"stopping" ,"timed-out" ,"toner-empty" ,"toner-low" , };"cups-pki-expired" enum {WebPrintJobState ,"preliminary" ,"pending" ,"processing" ,"completed" ,"canceled" };"aborted"
8. 개인정보 보호 및 보안 고려 사항
8.1. 잠재적 문제
8.1.1. 핑거프린팅
WebPrinter
객체는 핑거프린팅에 사용될 수 있는
printerName
및 printerId
attributes를
노출합니다.
8.1.2. 인쇄 작업 위조
악성 코드 주입은 다음으로 이어질 수 있습니다.
-
원치 않는 인쇄 작업이 프린터에 제출되는 것,
-
프린터에 대한 서비스 거부 공격.
이는 격리된 컨텍스트에서는 절대 발생해서는 안 됩니다.
8.1.3. 감시
애플리케이션은 프린터가 언제 사용 중인지 잠재적으로 관찰할 수 있습니다.
8.2. 완화 요소
8.2.1. Permissions Policy
이 명세는
문자열 "web-printing"으로 식별되는 정책 제어 기능을 정의합니다.
그 기본 허용 목록은 "self"입니다.
document의 permissions policy는
해당 문서의 모든 콘텐츠가 getPrinters()를
사용할 수 있는지 여부를 결정합니다.
어떤 문서에서든 비활성화된 경우, 해당 문서의 어떤 콘텐츠도
getPrinters()를
사용하도록 허용되지 않습니다.
8.2.2. 사용자 동의
프린터에 대한 접근은 강력한 기능입니다. 사용자 에이전트는
명시적 권한 없이
웹 애플리케이션이 WebPrinter
객체에 접근할 수 있도록 허용해서는 안 됩니다.
사용자 동의는
특정 origin에 대해 얻어야 합니다.
동의 요청은
getPrinters()
메서드 호출에 의해 트리거되어야 합니다. 사용자 에이전트는
어떤 origin이 접근을 요청하는지 명확히 나타내고
사용자가 충분한 정보를 바탕으로 결정을 내릴 수 있도록 충분한 정보를 제공하는 권한 프롬프트를
표시해야 합니다.
사용자 에이전트는 임시 (예: "이 세션에서만") 권한과 영구 권한 모두에 대한 옵션을 제공해야 합니다. 사용자가 영구 접근을 허용했다는 사실을 잊어버릴 위험을 완화하기 위해, 임시 권한이 기본값이자 더 눈에 띄는 옵션이어야 합니다.
사용자에게는 이 API에 대해 이전에 부여한 모든 권한을 보고 철회할 수 있는 메커니즘이 제공되어야 합니다.