summaryrefslogtreecommitdiff
path: root/devices
diff options
context:
space:
mode:
authorKen Sharp <ken.sharp@artifex.com>2022-12-07 16:40:14 +0000
committerKen Sharp <ken.sharp@artifex.com>2022-12-07 16:47:16 +0000
commit31753bd24be80831a7d1ee2da8a4d4f88d37cb40 (patch)
tree40ecded74b88c20a3f266b360d7b370d2b12df43 /devices
parent7a5a41ada38df5a809cf335eeef83357818d11a9 (diff)
downloadghostpdl-31753bd24be80831a7d1ee2da8a4d4f88d37cb40.tar.gz
GhostPDF + pdfwrite - Fix gstate inheritance for Type 3 font CharProcs
This started off with the file ContentStreamNoCycleType3insideType3.pdf from the pdfa.org 'safedocs' project. We were not rendering the pattern the way the documentation appeared to expect, nor the same as Acrobat. The remainder of this commit log is lengthy.... To start with it was necessary to decide what we 'should' be doing according to the specification, since Acrobat does not apparently do what is expected. This is the text of my findings; ---------------------------------------------------------------------- The file consists of a single page whose content stream draws two glyphs both in a type 3 font. One does nothing but draw a green triangle, we can ignore that one and concentrate on the other. The 'a' glyph from the type 3 font FType3A strokes a rectangle in red and then draws three 'c' glyphs from another font. In this case another type 3 font FType3B. The 'c' glyph CharProc sets text rendering mode 2, a blue stroke colour and a Pattern fill colour, and then draws the text 'ab' using the font Helvetica. So we expect the 'ab' text to be outlines in blue and filled with the pattern. The Pattern sets the fill colour to magenta and then draws the text 'ba' using a font named CyclicFont which is an alias for Helvetica again. From page 422 of the 1.7 PDF Reference: "Aside from the CTM, the graphics state is inherited from the environment of the text-showing operator that caused the glyph description to be invoked." So looking at the first type 3 CharProc, we see that the gstate has text rendering mode 2, the graphics state is otherwise the default (eg colours fill=stroke=DeviceGray,0). So we run the 'a' glyph from /FType3A which sets the linewidth to 20 and the stroke colour to pure red and does a stroke: 20 w 1 0 0 RG 0 0 750 750 re s We then draw the glyph 'c' three times from FType3B. This sets the text rendering mode to 2, sets the line width to 10 and sets the stroke colour to blue (0 0 1 RG). We then set the fill colour space to Pattern and use Pattern P1. The Pattern draws the text 'ab'. From step 2 of the pattern drawing sequence on page 294: "2.Installs the graphics state that was in effect at the beginning of the pattern’s parent content stream, with the current transformation matrix altered by the pattern matrix as described in Section 4.6.1, “General Properties of Patterns” " So we have three possible 'parent' content streams at this point; the Page, the first type 3 font and the second type 3 font. Depending on whether we consider a type 3 font CharProc to be a 'content stream'. Reading page 151 about content streams we see: "Each page of a document is represented by one or more content streams. Content streams are also used to package sequences of instructions as self-contained graphical elements, such as forms (see Section 4.9, "Form XObjects"), patterns (Section 4.6 "Patterns"), certain fonts (Section 5.5.4 "Type 3 Fonts"), and annotation appearances (Section 8.4.4 "Appearance Streams")." So obviously each of our type 3 content streams is a 'parent' as far as the Pattern is concerned. Therefore the graphics state the Pattern inherits should be the graphics state at the time the 'c' glyph from font FType3B is executed. So starting from the beginning of the page content stream and continuing until we get to the start of the 'c' glyph the following are encountered (ignoring CTM changes) 2 Tr % Fill then Stroke Text render mode 20 20 Td 50 Tw % start of 'a' glyph from font Ftype3A 20 w 1 0 0 RG So the 'ab' text should be filled and then stroked (Tr 2), it should be filled with Magenta (as per the Pattern explicit colour) and stroked with red, because that's the stroke colour at the time the CharProc is executed. This does not appear to be what Acrobat does. It seems to be drawing the 'ab' text as a simple magenta fill. Which would only be possible if the Pattern inherited the graphics state at the start of the Page, because after that point all text is rendered in Text rendering mode 2. Removing the '2 Tr' from the content stream for the 'c' glyph drops the blue strokes, so it does indeed look like this is what is happening..… Indeed, wrapping the original page content stream in a Form XObject, so that we can control the graphics state at the start of the Form, demonstrates that this is, in fact, exactly what Acrobat is doing. It does not matter what the graphics state is at the start of the Form (the parent content stream if we ignore the CharProcs), it still draws the Pattern with the graphics state at the start of the Page. ---------------------------------------------------------------------- The reason this wasn't happening was because we had specifically coded a hack into the type 3 CharProc processing which made the stroke colour the same as the fill colour at the start of every type 3 CharProc. Why ? Because that's what the old PS-based interpreter did, and it appears to be what Acrobat does. What was happening is that we were running a PCL file with the stick font. That font, unlike 'normal' fonts, is always stroked, not filled. That means that in PDF we need the colour that was set in the PCL file to be the stroke colour, but the pdfwrite device was writing the colour as the fill colour instead (because fonts are usually filled). In fact you can get the same effect with PostScript input too, any input where the act of setting a colour is expected to affect both stroke and fill, but the text uses a stroke. Acrobat draws the output from pdfwrite 'correctly', ie the same as the PCL input, and the old PS-based PDF interpreter had been modified to do so too, which is why we modified the pdfi interpreter the same way. But in fact this is incorrect; Acrobat is clearly wrong in doing this, though it is possible it only does it if the stroke colour is not explicitly set. So, in this commit we remove the hackery from the pdfi interpreter which allows the safedocs file to run correctly. We also update pdfwrite so that when writing the colour for a text operation if the font type is 3 (might do any operation) we write the current colour as both the stroke and fill colour. It is just possible that we might need to take the text rendering mode into account as well (might need to write the actual stored stroke colour) but I'm going to wait until someone comes up with a test file for that..... This shows differences with a small number of files, some are tiny text position differences (no idea why) and some are differences with type 3 fonts and pdfwrite which seem to be progressions.
Diffstat (limited to 'devices')
-rw-r--r--devices/vector/gdevpdtt.c13
1 files changed, 9 insertions, 4 deletions
diff --git a/devices/vector/gdevpdtt.c b/devices/vector/gdevpdtt.c
index 5d58e4766..c3e5574cd 100644
--- a/devices/vector/gdevpdtt.c
+++ b/devices/vector/gdevpdtt.c
@@ -475,11 +475,16 @@ pdf_prepare_text_drawing(gx_device_pdf *const pdev, gs_text_enum_t *pte)
return code;
}
- if (!pdev->ForOPDFRead) {
+ /* For ps2write output, and for any 'type 3' font we need to write both the stroke and fill colours
+ * because we don't know whether the font will use stroke or fill, or both, and expect the
+ * current colour to work for both operations.
+ */
+ if (!pdev->ForOPDFRead && font->FontType != ft_user_defined && font->FontType != ft_CID_user_defined
+ && font->FontType != ft_PCL_user_defined && font->FontType != ft_GL2_531 && font->FontType != ft_PDF_user_defined) {
if (pgs->text_rendering_mode != 3 && pgs->text_rendering_mode != 7) {
- if (font->PaintType == 2) {
- /* Bit awkward, if the PaintType is 2 then we want to set the
- * current ie 'fill' colour, but as a stroke colour because we
+ if (font->PaintType == 2 || font->FontType == ft_GL2_stick_user_defined) {
+ /* Bit awkward, if the PaintType is 2 (or its the PCL stick font, which is stroked)
+ * then we want to set the current ie 'fill' colour, but as a stroke colour because we
* will later change the text rendering mode to 1 (stroke).
*/
code = gx_set_dev_color(pgs);