From bf2ab0de927dae4a1dd67a0cbacefabaf0e4255b Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Mon, 15 May 2006 10:04:53 -0700 Subject: [PATCH] PDF: Add Type3 font support to PDF output. This uses the recently added cairo-scaled-font-subsets interface in a style very similar to what the PS surface does. --- src/cairo-pdf-surface.c | 242 +++++++++++++++++++++-- test/select-font-face-pdf-argb32-ref.png | Bin 2781 -> 2782 bytes 2 files changed, 231 insertions(+), 11 deletions(-) diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index f55db0466..a48182d28 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -38,7 +38,7 @@ #include "cairoint.h" #include "cairo-pdf.h" -#include "cairo-font-subset-private.h" +#include "cairo-scaled-font-subsets-private.h" #include "cairo-ft-private.h" #include "cairo-paginated-surface-private.h" #include "cairo-path-fixed-private.h" @@ -94,6 +94,12 @@ typedef struct _cairo_pdf_resource { unsigned int id; } cairo_pdf_resource_t; +typedef struct _cairo_pdf_font { + unsigned int font_id; + unsigned int subset_id; + cairo_pdf_resource_t subset_resource; +} cairo_pdf_font_t; + typedef struct _cairo_pdf_surface { cairo_surface_t base; @@ -113,6 +119,9 @@ typedef struct _cairo_pdf_surface { cairo_array_t streams; cairo_array_t alphas; + cairo_scaled_font_subsets_t *font_subsets; + cairo_array_t fonts; + cairo_pdf_resource_t next_available_resource; cairo_pdf_resource_t pages_resource; @@ -128,7 +137,8 @@ typedef struct _cairo_pdf_surface { cairo_paginated_mode_t paginated_mode; } cairo_pdf_surface_t; -#define PDF_SURFACE_DPI_DEFAULT 300 +#define PDF_SURFACE_DPI_DEFAULT 300 +#define PDF_SURFACE_MAX_GLYPHS_PER_FONT 256 static cairo_pdf_resource_t _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface); @@ -162,6 +172,9 @@ _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface); static cairo_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); +static cairo_status_t +_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); + static const cairo_surface_backend_t cairo_pdf_surface_backend; static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; @@ -264,6 +277,15 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_array_init (&surface->streams, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->alphas, sizeof (double)); + surface->font_subsets = _cairo_scaled_font_subsets_create (PDF_SURFACE_MAX_GLYPHS_PER_FONT); + if (! surface->font_subsets) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + free (surface); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); + surface->next_available_resource.id = 1; surface->pages_resource = _cairo_pdf_surface_new_object (surface); @@ -538,6 +560,8 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_pdf_surface_close_stream (surface); + _cairo_pdf_surface_emit_font_subsets (surface); + _cairo_pdf_surface_write_pages (surface); info = _cairo_pdf_surface_write_info (surface); @@ -570,6 +594,13 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->streams); _cairo_array_fini (&surface->alphas); + if (surface->font_subsets) { + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + surface->font_subsets = NULL; + } + + _cairo_array_fini (&surface->fonts); + return status; } @@ -1541,7 +1572,8 @@ static void _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t page, *res; - int num_pages, i; + cairo_pdf_font_t font; + int num_pages, num_fonts, i; int num_alphas, num_resources; double alpha; @@ -1612,6 +1644,15 @@ _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) " >>\r\n"); } + _cairo_output_stream_printf (surface->output," /Font <<\r\n"); + num_fonts = _cairo_array_num_elements (&surface->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&surface->fonts, i, &font); + _cairo_output_stream_printf (surface->output, " /CairoFont-%d-%d %d 0 R\r\n", + font.font_id, font.subset_id, font.subset_resource.id); + } + _cairo_output_stream_printf (surface->output, " >>\r\n"); + _cairo_output_stream_printf (surface->output, " >>\r\n"); @@ -1625,6 +1666,162 @@ _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) surface->height); } +static void +_cairo_pdf_surface_emit_glyph (cairo_pdf_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index, + unsigned int subset_glyph_index, + cairo_pdf_resource_t *glyph_ret) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS| + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + /* + * If that fails, try again but ask for an image instead + */ + if (status) + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS| + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (status) { + _cairo_surface_set_error (&surface->base, status); + return; + } + + /* XXX: Need to actually use the image not the path if that's all + * we could get... */ + + *glyph_ret = _cairo_pdf_surface_open_stream (surface, NULL); + + _cairo_output_stream_printf (surface->output, + "0 0 %f %f %f %f d1\r\n" + " \r\n", + _cairo_fixed_to_double (scaled_glyph->bbox.p1.x), + -_cairo_fixed_to_double (scaled_glyph->bbox.p2.y), + _cairo_fixed_to_double (scaled_glyph->bbox.p2.x), + -_cairo_fixed_to_double (scaled_glyph->bbox.p1.y)); + + status = _cairo_path_fixed_interpret (scaled_glyph->path, + CAIRO_DIRECTION_FORWARD, + _cairo_pdf_path_move_to, + _cairo_pdf_path_line_to, + _cairo_pdf_path_curve_to, + _cairo_pdf_path_close_path, + surface->output); + + _cairo_output_stream_printf (surface->output, + " f"); + + _cairo_pdf_surface_close_stream (surface); + + if (status) + _cairo_surface_set_error (&surface->base, status); +} + +static void +_cairo_pdf_surface_emit_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_pdf_surface_t *surface = closure; + cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource; + cairo_pdf_font_t font; + int i; + + glyphs = malloc (font_subset->num_glyphs * sizeof (cairo_pdf_resource_t)); + if (glyphs == NULL) { + _cairo_surface_set_error (&surface->base, CAIRO_STATUS_NO_MEMORY); + return; + } + + for (i = 0; i < font_subset->num_glyphs; i++) { + _cairo_pdf_surface_emit_glyph (surface, + font_subset->scaled_font, + font_subset->glyphs[i], i, + &glyphs[i]); + } + + encoding = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\r\n" + "<< /Type /Encoding\r\n" + " /Differences [0", encoding.id); + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " /%d", i); + _cairo_output_stream_printf (surface->output, + "]\r\n" + ">>\r\n" + "endobj\r\n"); + + char_procs = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\r\n" + "<<\r\n", char_procs.id); + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " /%d %d 0 R\r\n", + i, glyphs[i].id); + _cairo_output_stream_printf (surface->output, + ">>\r\n" + "endobj\r\n"); + + subset_resource = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\r\n" + "<< /Type /Font\r\n" + " /Subtype /Type3\r\n" + " /FontBBox [0 0 0 0]\r\n" + " /FontMatrix\t[1 0 0 1 0 0]\r\n" + " /Encoding %d 0 R\r\n" + " /CharProcs %d 0 R\r\n" + " /FirstChar 0\r\n" + " /LastChar %d\r\n", + subset_resource.id, + encoding.id, + char_procs.id, + font_subset->num_glyphs - 1); + + _cairo_output_stream_printf (surface->output, + " /Widths ["); + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, " 0"); + _cairo_output_stream_printf (surface->output, + "]\r\n"); + + _cairo_output_stream_printf (surface->output, + ">>\r\n" + "endobj\r\n"); + + font.font_id = font_subset->font_id; + font.subset_id = font_subset->subset_id; + font.subset_resource = subset_resource; + _cairo_array_append (&surface->fonts, &font); +} + +static cairo_status_t +_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) +{ + cairo_status_t status; + + status = _cairo_scaled_font_subsets_foreach (surface->font_subsets, + _cairo_pdf_surface_emit_font_subset, + surface); + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + surface->font_subsets = NULL; + + if (status) + return status; + + return CAIRO_STATUS_SUCCESS; +} + #if 0 static cairo_status_t _cairo_pdf_surface_write_fonts (cairo_pdf_surface_t *surface) @@ -2125,6 +2322,14 @@ _cairo_pdf_surface_fill (void *abstract_surface, return status; } +static char +hex_digit (int i) +{ + i &= 0xf; + if (i < 10) return '0' + i; + return 'a' + (i - 10); +} + static cairo_int_status_t _cairo_pdf_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, @@ -2134,8 +2339,10 @@ _cairo_pdf_surface_show_glyphs (void *abstract_surface, cairo_scaled_font_t *scaled_font) { cairo_pdf_surface_t *surface = abstract_surface; - cairo_path_fixed_t path; + int current_subset_id = -1; + unsigned int font_id, subset_id, subset_glyph_index; cairo_status_t status; + int i; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _analyze_operation (surface, op, source); @@ -2146,14 +2353,27 @@ _cairo_pdf_surface_show_glyphs (void *abstract_surface, if (status) return status; - _cairo_path_fixed_init (&path); - _cairo_scaled_font_glyph_path (scaled_font, glyphs, num_glyphs, &path); - status = _cairo_pdf_surface_fill (surface, op, source, - &path, CAIRO_FILL_RULE_WINDING, - 0.1, scaled_font->options.antialias); - _cairo_path_fixed_fini (&path); + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets, + scaled_font, glyphs[i].index, + &font_id, &subset_id, &subset_glyph_index); + if (status) + return status; - return status; + if (subset_id != current_subset_id) { + _cairo_output_stream_printf (surface->output, + "/CairoFont-%d-%d 1 Tf\r\n", + font_id, subset_id); + current_subset_id = subset_id; + } + _cairo_output_stream_printf (surface->output, + "BT %f %f Td <%c%c> Tj ET\r\n", + glyphs[i].x, glyphs[i].y, + hex_digit (subset_glyph_index >> 4), + hex_digit (subset_glyph_index)); + } + + return _cairo_output_stream_get_status (surface->output); } static void diff --git a/test/select-font-face-pdf-argb32-ref.png b/test/select-font-face-pdf-argb32-ref.png index b2de418273822d01d6fb90c1db7ce03ee475484b..9b8c07a465e1c4a202a24aba82d941c0dfff9a10 100644 GIT binary patch delta 2701 zcmWkwc_7pMAODi@E5Ar`6wzZLPY==_jf|3WIck^Sj*d0(&N%{@GEIP;7IB@1o=o*0u1QB%&ImLK*{ z23VnHnKZQ)FFnMiWVB&ucxe2jrSm5(y;dig(vqsmM z?bQxV8Tn(l@7q&giu}HP`#!SSmOBF1qSY_lhLy(kUX-21S)ib9(VwVRB?Ut99O`fLr@xzDTNC1Pu!d9>~DKAfxV0A@5yCG&pBo>E< zhihtRq-SJ+yli*n2YT74Y!t(nDQ%JGQpA>+T3{EImX^jfS?KErvtK#^{TvQqHrC$0 zv$gd+7K_DT1~)bkY0Qd|5Cn0u5l?n5+TEDVn3$L_Kh{ToJ+#zNU;mS~HlMpHo&;MW zc@IF_!s%OxoSdAo+He^l8^swNt;es=y?Uh|6C);(6blDp7Y9#PjXcZE)i`#{!sUH1 z<2f?ErmBi;TS!PvRaRD(JRcn`PtPc4KeejU3bI3%a8V~~Ichp(>_g{ff z*wfv;E)-rb99nY8HH%*ONF#_J-n(}TN-PjKIy-;gUOhgBsDb^XU6qjZEI`m{>#v?@edFL zt*xzneSKqN-=5d%4}y2ry?y((tjqw3+tbAp@fltI0sj8r;LuQ^Q%zkR^y5ZnmiFGg zdlM5A_X~4y$AR)NKen4$B!TX{WnTM>VkrM znK%B5_`?U39244hRb{2(p+gy(mInYbnVglCm7JUmAuO_Pd%e%nt~W11MK^G6vOb`&VmkDfI{LL9ay!Bypu zNM!MECaQ8U2a!O4;Ld#<9`2l71(in~NH~Q0?CfkxO3F9!%I?fV!^|^jO#GJMqytG? zTN~(Svs0ecd4~lC^34g=)z#bEcS<;BePI*uUAeCu7+y#fjaZ8_m(bUu=!TRzn!Hm?@HXrjl8(;s) zInzPZh`)kWekhzD(57SlrcmZ?A%fsXE+`o1oYK*eHie%(drtFLz&!SA60D0#Jub(z zCsmJxK#ba1uAR(e9u04d5H4OF9aoDJ{W~HO-3Abe+p#zt?#2ymFF|{I`&@m~_pPPk z=`UZ1qc}4SrV$zJ@;A;wqtSp5w?3M~;Xpv3)9Kg*A%}V~4ejbGah{GL5{U)19YJv7 zqeqU8joprrJt+1AEJey6-TBLA{pV^dSi^5_ZWhb+I-i$7E-GsM>(}`&Uu;D)Gk!Gm{{R~sn+mX^qH}(6 z@q=FlkYq(Q%5kh3dF@t`d-xVaZ?Bbslr^zf27n}c76F(iZa>kI4XnIy;R2J%>~qW? z9v+^W@_}L8-Q8VX*+WD8nVB`FyyZX0@A=3?-=n}hZYxbQQ5MjASl}uKi!C1r+WPkGB&hTOLg}AY)81g9+~-Mbsw)~T zS{{=DybWSj-`m;QT{+sZ8ol(1T5;`(O5ExrTkQ`C0RgEjKjgZ`e9zfQC0QxS&0DN)%Az9596-R*UkG-eww5)zkj zE?3hs(fr)GOEmPr8D!MlBW@Vh+Np5BFDSH`874S`MtS@YwwLt!C+EN>qpXU=@?r( zJ2o`NFmC-`!2FmCOwqK-=BEL|eifp+>CDQCB?5skGSVV<#hO32qP9kOQ!hI@IYCzS zTRrZ6$1G=X@JY`cWD!yP-k$%v7qOdmzkh>dJTx?vMx$Z$gNaOHEDy-a z8XZYjKLE;vPWGM9)#Yyqm!RV*po-g^5a0J4dG+emJ|X0Llx`k$IfT{jm%7X2F)|^a zFM9DpN=D|}#)g8_K!5+6d+X3NNF-A8=G;?A>VH@(w?4bFQ_`n)tfResIK(8?x0|>E zIip!_Z+G|2vU^?r9~W@V&CNlC_ytGM1d;r%g=S{f(O%8z~x0^Zt8pA)RVyXgTF1MVw``+4DDgW)SKJO5M7rZ>h>j w1E)-q-8^}kn?u_wH6M0$OAZBP@7~`9RtDN$u%`t_04N-6oUO~Od?fDv2X)PCfdBvi delta 2699 zcmWlbc|4T+7snq$?xeCO+lUhGM2&q7X_VX%X|ml^#?sg$Wa*hqNWa2VOd5t_EHTyw zA;WduscTDJyRk%fEEy%c8Ghe;{(Js8-`Dw^&v}2IaXF4$hQ3fH*}w`uIw~A=SUz*= z;1K>E)Ddi!K|jp$&_Z9h=b|U@9o^muuMyKmU%e_bek^{+EP)r%vVMnWx}L)wdm^4l zG#qf#Uci|X4R<*FK+Ozn@#ne7pC`mfHVj-P&O+}{F1EOzaK-##X4L@*fk5!a-!+Yp zE!aqyAHMPZI=lCv6}*w3pRZoUws{qmvW0``>%_gi?Ades9wCZfSN9YQY~sS_PrA zao}NVXDwM*o&Jz|b8qVN=d-i3RUQm4-RRFnzd`w~I(zrRdj)*{xuZ|KbSIjkD_Jbq z-kv=)O7!ogF`3Mp%#o6kk`tbFkacvlaoY5e zBS%Oil0YCJlgTnNGAQ{@0`rT>-GzY@I+8E4k;v!6xQ)+N!i#%T1qeot0>#0s+Y{U2d`qN1W=Vm5@O z{?~D9hrd}-p^He8R#fzGa}$RQ4Gq_Moc8c|cAAQ*scA?^2z1fgn-xC&S5FW2t(U5* zDvd&^zblA^Hr7T%E4^7E28yJ_M8v***^d9hul9T&^oB3vaKhGj{LI?oC`I)|sq4t_ z@XcyBb#-+#8huh?fyd)5EChyzPOxAWwm9zlq|Mp0NhvApSDe$EGcpqGvWSi!)9ho? z=WpD&VQVW4Nk~Yz;&23}hgDu)Ru)E`3|XV(krpu*)yWwjW-b+DgE@_IP&uy54pyI= zpI=^G?e6USxw`s0uuE@_X~fsR<+$5t?d%#F8)IW*HIEdQ0 z?AaqFC6$zvG&MD4q3T3;Il}$&<;&LA*2+p`LErO?jEuazyo`*h2yV~DMhvN~%DS+* zxtYl{G&lD=D$Ve$?|F%mMSutFF&Jg@Z{1YmiMS<8vs@aP-1iawNHiMG%E|&Aeb{Mo zva%bSo2CoqttTI`r-9ABx=OKjwiaHVNvWx+nYncbgAs!)V!r3s)S$JswG9pXgN%Uv z%dS+ml*IY^9!tKip`qdK?w*>OdfbU7LM$yUegFP_Yiny)mt(2x)+RSkgWGGhUEbWx zpY1DvGH5h7?GfRiNc9$U4Hu6jxs$|je7c_&{ancA(~{{7M`Ofe$P51 zGc&2;EB16_PveZVslJ>ylRmU^8qGC$rsbTA*z^1K@h2JGR&m5wsv>J%&erc(*Dh5Q zpC4fG!uDm@w|zoFLb3=^=p(M_&QCu)`(zM-;84&fo@9%^83YG)b$82&i}d&R$6M|G znJN>v_N{t2C_6hldTE?f7p&wlQ(ROu8)*iKh>E^U1G;G~pGi6J0gnz1U9yZ@16^KT ze9(x>%e%W(T3+5omWA9m9iEG)n^XS{1@aMPvi zv37QL4hn)%=*GrIw1NbrMm`s5#tAUc$#k$6RaQ`VFc7zr&ld=Uh#&A@Ob$R!G>cQz z?le3=7Y2i&QLU@1tJ~Vz&Xu&ZwA9tru~-uw9bZ$Bcb~(2o4Do(C8)%89(;;JT8Vr1 z9{97P!_deG=js{{HehLK8EIx|WVH5e7_NQKs;J@2vJ;Q8E1#@MdyyGUXr=P&|HDupS;Bo~~-?q9qGj0!QG{!$C#` zrKB*LvV>T{+qdpz{o~DdE}`U`8XEFd;y3T#zn>qY+;Qp58Il8CQ9(hC44+$HngIOy zDNs2j3B!l6=g*slO+rxB@He{5f13|hW*Y|$5&!Bj)m!;8w;KCw``b4ZsV#x03eLawpoD4{srEN=hY2Qh9)Z7K=h-%91 z_Vf4kecRM@`pz;wBqYsIv%o)=H)cxhuDk!jfsW301YyHYM;_e2KY_pT1E$J8{SbLH z3FVY$bvr!#2>G0d%{lrf*i~FY!VaPLdP2%e7p`YAZ{NBl4FSqTEsYD*og~_AZEZiA zSGtq}-bF?l(Cg~fz{NtXQ(+>)_u4(xB)u9V=Z30%c6N5o03k^v>D2q}Q}6L;WwtMR z7(m(Edm{7KD<;a=;9y2y0Dx9w*wi2VFhj-e?IjjcUVZ=&#lyov^C?&zyw$14I;yFO zv9|{*a=F~6=Z>b@C9e4U`-=(-nH?6g2FVK|_2$hv&>4gI%O06vuq#`qbyT|a&6{Vv z+`Xcr?x?I|s_akCC=_F3jPN6w2U3A2&!*3Je+@y-uO0f{69=6H6#}S8U=`;L6-l y@{cilh;+@5ih+EM-50xm3~cP;gM}GCPX9nUs