diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3drivers-pdf.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3drivers-pdf.dtx | 401 |
1 files changed, 352 insertions, 49 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3drivers-pdf.dtx b/Master/texmf-dist/source/latex/l3kernel/l3drivers-pdf.dtx index a1275e4f4fc..8a5ed3e49b9 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3drivers-pdf.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3drivers-pdf.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2019-04-21} +% \date{Released 2019-05-03} % % \maketitle % @@ -60,6 +60,12 @@ %<@@=driver> % \end{macrocode} % +% Setting up PDF resources is a complex area with only limited documentation +% in the engine manuals. The following code builds heavily on existing ideas +% from \pkg{hyperref} work by Sebastian Rahtz and Heiko Oberdiek, and +% significant contributions by Alexander Grahn, in addition to the specific +% code referenced a various points. +% % \subsection{Shared code} % % A very small number of items that belong at the driver level but which @@ -76,12 +82,39 @@ % \end{macrocode} % \end{variable} % +% \begin{variable}{\l_@@_pdf_tmp_box} +% For fitting destinations. +% \begin{macrocode} +\box_new:N \l_@@_pdf_tmp_box +% \end{macrocode} +% \end{variable} +% % \subsection{\texttt{dvips} driver} % % \begin{macrocode} %<*dvips> % \end{macrocode} % +% \begin{macro}{\@@_pdfmark:n, \@@_pdfmark:x} +% Used often enough it should be a separate function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_pdfmark:n #1 + { \@@_postscript:n { mark #1 ~ pdfmark } } +\cs_generate_variant:Nn \@@_pdfmark:n { x } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Catalogue entries} +% +% \begin{macro}{\driver_pdf_catalog_gput:nn, \driver_pdf_info_gput:nn} +% \begin{macrocode} +\cs_new_protected:Npn \driver_pdf_catalog_gput:nn #1#2 + { \@@_pdfmark:n { { Catalog } << /#1 ~ #2 >> /PUT } } +\cs_new_protected:Npn \driver_pdf_info_gput:nn #1#2 + { \@@_pdfmark:n { /#1 ~ #2 /DOCINFO } } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Objects} % % \begin{variable}{\g_@@_pdf_object_int, \g_@@_pdf_object_prop} @@ -100,12 +133,12 @@ { \int_gincr:N \g_@@_pdf_object_int \int_const:cn - { g_@@_pdf_object_ \tl_to_str:n {#1} _int } + { c_@@_pdf_object_ \tl_to_str:n {#1} _int } { \g_@@_pdf_object_int } \prop_gput:Nnn \g_@@_pdf_object_prop {#1} {#2} } \cs_new:Npn \driver_pdf_object_ref:n #1 - { { driver.obj \int_use:c { g_@@_pdf_object_ \tl_to_str:n {#1} _int } } } + { { driver.obj \int_use:c { c_@@_pdf_object_ \tl_to_str:n {#1} _int } } } % \end{macrocode} % \end{macro} % \end{macro} @@ -123,9 +156,9 @@ % \begin{macrocode} \cs_new_protected:Npn \driver_pdf_object_write:nn #1#2 { - \@@_postscript:x + \@@_pdfmark:x { - mark /_objdef ~ \driver_pdf_object_ref:n {#1} + /_objdef ~ \driver_pdf_object_ref:n {#1} /type \str_case_e:nn { \prop_item:Nn \g_@@_pdf_object_prop {#1} } @@ -135,7 +168,7 @@ { fstream } { /stream } { stream } { /stream } } - /OBJ ~ pdfmark + /OBJ } \use:c { @@_pdf_object_write_ \prop_item:Nn \g_@@_pdf_object_prop {#1} :nn } @@ -143,18 +176,18 @@ } \cs_new_protected:Npn \@@_pdf_object_write_array:nn #1#2 { - \@@_postscript:x + \@@_pdfmark:x { - mark ~ \driver_pdf_object_ref:n {#1} - [ ~ \exp_not:n {#2} ~ ] ~ /PUTINTERVAL ~ pdfmark + \driver_pdf_object_ref:n {#1} + [ ~ \exp_not:n {#2} ~ ] ~ /PUTINTERVAL } } \cs_new_protected:Npn \@@_pdf_object_write_dict:nn #1#2 { - \@@_postscript:x + \@@_pdfmark:x { - mark ~ \driver_pdf_object_ref:n {#1} - << \exp_not:n {#2} >> /PUT ~ pdfmark + \driver_pdf_object_ref:n {#1} + << \exp_not:n {#2} >> /PUT } } \cs_new_protected:Npn \@@_pdf_object_write_stream:nn #1#2 @@ -198,8 +231,9 @@ % \begin{macro} % { % driver.cvs , -% driver.rect.ht , -% driver.pt.dvi +% driver.dvi.pt , +% driver.pt.dvi , +% driver.rect.ht % } % Small utilities for PostScript manipulations. Conversion to DVI dimensions % is done here to allow for |Resolution|. The total height of a rectangle @@ -208,6 +242,7 @@ \@@_postscript_header:n { /driver.cvs { 65534 ~ string ~ cvs } def + /driver.dvi.pt { 72.27 ~ mul ~ Resolution ~ div } def /driver.pt.dvi { 72.27 ~ div ~ Resolution ~ mul } def /driver.rect.ht { dup ~ 1 ~ get ~ neg ~ exch ~ 3 ~ get ~ add } def } @@ -228,11 +263,15 @@ % % \begin{macro} % { -% driver.annotation.border , -% driver.annotation.ll , -% driver.annotation.ur , -% driver.link.ll , -% driver.link.ur , +% driver.rect , +% driver.save.ll , +% driver.save.ur , +% driver.save.linkll , +% driver.save.linkur , +% driver.llx , +% driver.lly , +% driver.urx , +% driver.ury % } % Functions for marking the limits of an annotation/link, plus drawing the % border. We separate links for generic annotations to support adding a @@ -240,23 +279,23 @@ % \begin{macrocode} \@@_postscript_header:n { - /driver.annotation.border + /driver.rect { /Rect [ driver.llx ~ driver.lly ~ driver.urx ~ driver.ury ] } def - /driver.annotation.ll + /driver.save.ll { currentpoint /driver.lly ~ exch ~ def /driver.llx ~ exch ~ def } def - /driver.annotation.ur + /driver.save.ur { currentpoint /driver.ury ~ exch ~ def /driver.urx ~ exch ~ def } def - /driver.link.ll + /driver.save.linkll { currentpoint ~ driver.linkmargin ~ add ~ @@ -266,7 +305,7 @@ /driver.llx ~ exch ~ def } def - /driver.link.ur + /driver.save.linkur { currentpoint ~ driver.linkmargin ~ sub ~ @@ -282,6 +321,68 @@ % % \begin{macro} % { +% driver.dest.anchor , +% driver.dest.x , +% driver.dest.y , +% driver.dest.point , +% driver.dest2device , +% driver.dev.x , +% driver.dev.y , +% driver.tmpa , +% driver.tmpb , +% driver.tmpc , +% driver.tmpd +% } +% For finding the anchor point of a destination link. We make the use case +% a separate function as it comes up a lot, and as this makes it easier to +% adjust if we need additional effects. We also need a more complex approach +% to convert a co-ordinate pair correctly when defining a rectangle: this +% can otherwise be out when using a landscape page. (Thanks to Alexander +% Grahn for the approach here.) +% \begin{macrocode} +\@@_postscript_header:n + { + /driver.dest.anchor + { + currentpoint ~ exch ~ + driver.dvi.pt ~ 72 ~ add ~ + /driver.dest.x ~ exch ~ def ~ + driver.dvi.pt ~ + vsize ~ 72 ~ sub ~ exch ~ sub ~ + /driver.dest.y ~ exch ~ def + } + def + /driver.dest.point + { driver.dest.x ~ driver.dest.y } def + /driver.dest2device + { + /driver.dest.y ~ exch ~ def + /driver.dest.x ~ exch ~ def ~ + matrix ~ currentmatrix ~ + matrix ~ defaultmatrix ~ + matrix ~ invertmatrix ~ + matrix ~ concatmatrix ~ + cvx ~ exec + /driver.dev.y ~ exch ~ def + /driver.dev.x ~ exch ~ def + /driver.tmpd ~ exch ~ def + /driver.tmpc ~ exch ~ def + /driver.tmpb ~ exch ~ def + /driver.tmpa ~ exch ~ def ~ + driver.dest.x ~ driver.tmpa ~ mul ~ + driver.dest.y ~ driver.tmpc ~ mul ~ add ~ + driver.dev.x ~ add ~ + driver.dest.x ~ driver.tmpb ~ mul ~ + driver.dest.y ~ driver.tmpd ~ mul ~ add ~ + driver.dev.y ~ add + } + def + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { % driver.bordertracking , % driver.bordertracking.begin , % driver.bordertracking.end , @@ -407,7 +508,7 @@ currentpoint ~ driver.originy ~ ne ~ exch ~ driver.originx ~ ne ~ or { - driver.link.ll + driver.save.linkll /driver.lly ~ driver.lly ~ driver.outerbox ~ 1 ~ get ~ sub ~ def ~ driver.bordertracking.begin @@ -428,7 +529,7 @@ currentpoint ~ driver.originy ~ ne ~ exch ~ driver.originx ~ ne ~ or { - driver.link.ll + driver.save.linkll /driver.lly ~ driver.lly ~ driver.outerbox ~ 1 ~ get ~ sub ~ def ~ driver.bordertracking.begin @@ -672,37 +773,36 @@ \@@_pdf_annotation:nnnn {#1} {#2} {#3} {#4} \int_gincr:N \g_@@_pdf_object_int \int_gset_eq:NN \g_@@_pdf_annotation_int \g_@@_pdf_object_int - \@@_postscript:x + \@@_pdfmark:x { - mark - /_objdef { driver.obj \int_use:N \g_@@_pdf_object_int } - driver.annotation.border ~ - #4 ~ - /ANN ~ pdfmark + + /_objdef { driver.obj \int_use:N \g_@@_pdf_object_int } + driver.rect ~ + #4 ~ + /ANN } } \cs_new_protected:Npn \@@_pdf_annotation:nnnn #1#2#3#4 { \box_move_down:nn {#3} - { \hbox:n { \@@_postscript:n { driver.annotation.ll } } } + { \hbox:n { \@@_postscript:n { driver.save.ll } } } \hbox:n {#4} \box_move_up:nn {#2} { \hbox:n { \tex_kern:D \dim_eval:n {#1} \scan_stop: - \@@_postscript:n { driver.annotation.ur } + \@@_postscript:n { driver.save.ur } } } \int_gincr:N \g_@@_pdf_object_int \int_gset_eq:NN \g_@@_pdf_annotation_int \g_@@_pdf_object_int - \@@_postscript:x + \@@_pdfmark:x { - mark - /_objdef { driver.obj \int_use:N \g_@@_pdf_object_int } - driver.annotation.border ~ - \c_@@_pdf_AR_fix_tl - /ANN ~ pdfmark + /_objdef { driver.obj \int_use:N \g_@@_pdf_object_int } + driver.rect ~ + \c_@@_pdf_AR_fix_tl + /ANN } } % \end{macrocode} @@ -852,20 +952,20 @@ %</package> } \box_move_down:nn { \box_dp:N \l_@@_pdf_content_box } - { \hbox:n { \@@_postscript:n { driver.link.ll } } } + { \hbox:n { \@@_postscript:n { driver.save.linkll } } } \@@_postscript:n { driver.bordertracking.begin } \hbox_unpack:N \l_@@_pdf_content_box \@@_postscript:n { driver.bordertracking.end } \box_move_up:nn { \box_ht:N \l_@@_pdf_content_box } { \hbox:n - { \@@_postscript:n { driver.link.ur } } + { \@@_postscript:n { driver.save.linkur } } } \@@_postscript:x { mark \g_@@_pdf_link_dict_tl /Subtype /Link ~ - driver.annotation.border + driver.rect /ANN ~ driver.pdfmark } \@@_pdf_link_sf_restore: @@ -998,6 +1098,63 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{\driver_pdf_destination:nn, \driver_pdf_destination_rectangle:nn} +% Here, we need to turn the zoom into a scale. We also need to know where +% the current anchor point actually is: worked out in PostScript. For the +% rectangle version, we have a bit more PostScript: we need two points. +% \begin{macrocode} +\cs_new_protected:Npn \driver_pdf_destination:nn #1#2 + { + \@@_postscript:n { driver.dest.anchor } + \@@_pdfmark:x + { + /View + [ + \str_case:nnF {#2} + { + { xyz } { /XYZ ~ driver.dest.point ~ null } + { fit } { /Fit } + { fitb } { /FitB } + { fitbh } { /FitBH ~ driver.dest.y } + { fitbv } { /FitBV ~ driver.dest.x } + { fith } { /FitH ~ driver.dest.y } + { fitv } { /FitV ~ driver.dest.x } + } + { + /XYZ ~ driver.dest.point ~ \fp_eval:n { (#2) / 100 } + } + ] + /Dest ( \exp_not:n {#1} ) cvn + /DEST + } + } +\cs_new_protected:Npn \driver_pdf_destination_rectangle:nn #1#2 + { + \group_begin: + \hbox_set:Nn \l_@@_pdf_tmp_box {#2} + \box_move_down:nn + { \box_dp:N \l_@@_pdf_tmp_box } + { \hbox:n { \@@_postscript:n { driver.save.ll } } } + \box_use:N \l_@@_pdf_tmp_box + \box_move_up:nn + { \box_ht:N \l_@@_pdf_tmp_box } + { \hbox:n { \@@_postscript:n { driver.save.ur } } } + \@@_pdfmark:n + { + /View + [ + /FitR ~ + driver.llx ~ driver.lly ~ driver.dest2device ~ + driver.urx ~ driver.ury ~ driver.dest2device + ] + /Dest ( #1 ) cvn + /DEST + } + \group_end: + } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Structure} % % \begin{macro}{\driver_pdf_compresslevel:n} @@ -1142,6 +1299,70 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{\driver_pdf_destination:nn, \driver_pdf_destination_rectangle:nn} +% A simple task: pass the data to the primitive. The |\scan_stop:| deals +% with the danger of an unterminated keyword. The zoom given here is a +% percentage, but we need to pass it as \emph{per mille}. The rectangle +% version is also easy as everything is build in. +% \begin{macrocode} +\cs_new_protected:Npx \driver_pdf_destination:nn #1#2 + { + \cs_if_exist:NTF \tex_pdfextension:D + { \exp_not:N \tex_pdfextension:D dest ~ } + { \exp_not:N \tex_pdfdest:D } + name {#1} + \exp_not:N \str_case:nnF {#2} + { + { xyz } { xyz } + { fit } { fit } + { fitb } { fitb } + { fitbh } { fitbh } + { fitbv } { fitbv } + { fith } { fith } + { fitv } { fitv } + } + { xyz ~ zoom \exp_not:N \fp_eval:n { #2 * 10 } } + \scan_stop: + } +\cs_new_protected:Npx \driver_pdf_destination_rectangle:nn #1#2 + { + \group_begin: + \hbox_set:Nn \l_@@_pdf_tmp_box {#2} + \cs_if_exist:NTF \tex_pdfextension:D + { \exp_not:N \tex_pdfextension:D dest ~ } + { \exp_not:N \tex_pdfdest:D } + name {#1} + fitr ~ + width \exp_not:N \box_wd:N \l_@@_pdf_tmp_box + height \exp_not:N \box_ht:N \l_@@_pdf_tmp_box + depth \exp_not:N \box_dp:N \l_@@_pdf_tmp_box + \box_use:N \l_@@_pdf_tmp_box + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Catalogue entries} +% +% \begin{macro}{\driver_pdf_catalog_gput:nn, \driver_pdf_info_gput:nn} +% \begin{macrocode} +\cs_new_protected:Npx \driver_pdf_catalog_gput:nn #1#2 + { + \cs_if_exist:NTF \tex_pdfextension:D + { \tex_pdfextension:D catalog } + { \tex_pdfcatalog:D } + { / #1 ~ #2 } + } +\cs_new_protected:Npx \driver_pdf_info_gput:nn #1#2 + { + \cs_if_exist:NTF \tex_pdfextension:D + { \tex_pdfextension:D info } + { \tex_pdfinfo:D } + { / #1 ~ #2 } + } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Objects} % % \begin{variable}{\g_@@_pdf_object_prop} @@ -1163,7 +1384,7 @@ { #1 reserveobjnum ~ \int_const:cn - { g_@@_pdf_object_ \exp_not:N \tl_to_str:n {##1} _int } + { c_@@_pdf_object_ \exp_not:N \tl_to_str:n {##1} _int } {#2} \prop_gput:Nnn \exp_not:N \g_@@_pdf_object_prop {##1} {##2} } @@ -1172,12 +1393,12 @@ { \@@_tmp:w { \tex_pdfextension:D obj ~ } - { \tex_pdffeedback:D lastobj \scan_stop: } + { \exp_not:N \tex_pdffeedback:D lastobj \scan_stop: } } { \@@_tmp:w { \tex_pdfobj:D } { \tex_pdflastobj:D } } \group_end: \cs_new:Npn \driver_pdf_object_ref:n #1 - { \int_use:c { g_@@_pdf_object_ \tl_to_str:n {#1} _int } ~ 0 ~ R } + { \int_use:c { c_@@_pdf_object_ \tl_to_str:n {#1} _int } ~ 0 ~ R } % \end{macrocode} % \end{macro} % \end{macro} @@ -1194,7 +1415,7 @@ { \tex_immediate:D #1 useobjnum ~ \int_use:c - { g_@@_pdf_object_ \tl_to_str:n {##1} _int } + { c_@@_pdf_object_ \tl_to_str:n {##1} _int } \str_case_e:nn { \prop_item:Nn \g_@@_pdf_object_prop {##1} } { @@ -1333,6 +1554,17 @@ % \end{macrocode} % \end{macro} % +% \subsubsection{Catalogue entries} +% +% \begin{macro}{\driver_pdf_catalog_gput:nn, \driver_pdf_info_gput:nn} +% \begin{macrocode} +\cs_new_protected:Npn \driver_pdf_catalog_gput:nn #1#2 + { \@@_pdf:n { put ~ @catalog << /#1 ~ #2 >> } } +\cs_new_protected:Npn \driver_pdf_info_gput:nn #1#2 + { \@@_pdf:n { docinfo << /#1 ~ #2 >> } } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Objects} % % \begin{variable}{\g_@@_pdf_object_int, \g_@@_pdf_object_prop} @@ -1533,9 +1765,70 @@ % \end{macro} % % \begin{macro}{\driver_pdf_link_margin:n} -% Nope. +% Pass to \texttt{dvipdfmx}. +% \begin{macrocode} +\cs_new_protected:Npn \driver_pdf_link_margin:n #1 + { \@@_literal:x { dvipdfmx:config~g~ \dim_eval:n {#1} } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\driver_pdf_destination:nn, \driver_pdf_destination_rectangle:nn} +% Here, we need to turn the zoom into a scale. The method for \texttt{FitR} +% is from Alexander Grahn: the idea is to avoid needing to do any calculations +% in \TeX{} by using the driver data for \texttt{@xpos} and \texttt{@ypos}. % \begin{macrocode} -\cs_new_protected:Npn \driver_pdf_link_margin:n #1 { } +\cs_new_protected:Npn \driver_pdf_destination:nn #1#2 + { + \@@_pdf:x + { + dest ~ ( \exp_not:n {#1} ) + [ + @thispage + \str_case:nnF {#2} + { + { xyz } { /XYZ ~ @xpos ~ @ypos ~ null } + { fit } { /Fit } + { fitb } { /FitB } + { fitbh } { /FitBH } + { fitbv } { /FitBV ~ @xpos } + { fith } { /FitH ~ @ypos } + { fitv } { /FitV ~ @xpos } + } + { /XYZ ~ @xpos ~ @ypos ~ \fp_eval:n { (#2) / 100 } } + ] + } + } +\cs_new_protected:Npn \driver_pdf_destination_rectangle:nn #1#2 + { + \group_begin: + \hbox_set:Nn \l_@@_pdf_tmp_box {#2} + \box_move_down:nn { \box_dp:N \l_@@_pdf_tmp_box } + { + \hbox:n + { + \@@_pdf:n { obj ~ @driver_#1_llx ~ @xpos } + \@@_pdf:n { obj ~ @driver_#1_lly ~ @ypos } + } + } + \box_use:N \l_@@_pdf_tmp_box + \box_move_up:nn { \box_ht:N \l_@@_pdf_tmp_box } + { + \hbox:n + { + \@@_pdf:n + { + dest ~ (#1) + [ + @thispage + /FitR ~ + @driver_#1_llx ~ @driver_#1_lly ~ + @xpos ~ @ypos + ] + } + } + } + \group_end: + } % \end{macrocode} % \end{macro} % @@ -1589,6 +1882,16 @@ %<*dvisvgm> % \end{macrocode} % +% \subsubsection{Catalogue entries} +% +% \begin{macro}{\driver_pdf_catalog_gput:nn, \driver_pdf_info_gput:nn} +% No-op. +% \begin{macrocode} +\cs_new_protected:Npn \driver_pdf_catalog_gput:nn #1#2 { } +\cs_new_protected:Npn \driver_pdf_info_gput:nn #1#2 { } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Objects} % % \begin{macro}{\driver_pdf_object_new:nn} |