summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3drivers-pdf.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3drivers-pdf.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3drivers-pdf.dtx401
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}