2020-01-18 11:38:54 +01:00
// Copyright (C) 2019-2020 Jakub Melka
2019-02-24 17:48:37 +01:00
//
// This file is part of PdfForQt.
//
// PdfForQt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PdfForQt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
# include "pdfpagecontentprocessor.h"
# include "pdfdocument.h"
2019-04-29 17:03:19 +02:00
# include "pdfexception.h"
2019-05-07 18:21:22 +02:00
# include "pdfimage.h"
2019-08-25 18:16:37 +02:00
# include "pdfpattern.h"
2020-01-18 14:55:26 +01:00
# include "pdfexecutionpolicy.h"
2019-02-24 17:48:37 +01:00
2019-08-31 15:55:59 +02:00
# include <QPainterPathStroker>
2019-02-24 17:48:37 +01:00
namespace pdf
{
// Graphic state operators - mapping from PDF name to the enum, splitted into groups.
// Please see Table 4.1 in PDF Reference 1.7, chapter 4.1 - Graphic Objects.
//
// General graphic state: w, J, j, M, d, ri, i, gs
// Special graphic state: q, Q, cm
// Path construction: m, l, c, v, y, h, re
// Path painting: S, s, F, f, f*, B, B*, b, b*, n
// Clipping paths: W, W*
// Text object: BT, ET
// Text state: Tc, Tw, Tz, TL, Tf, Tr, Ts
// Text positioning: Td, TD, Tm, T*
// Text showing: Tj, TJ, ', "
// Type 3 font: d0, d1
// Color: CS, cs, SC, SCN, sc, scn, G, g, RG, rg, K, k
// Shading pattern: sh
// Inline images: BI, ID, EI
// XObject: Do
// Marked content: MP, DP, BMC, BDC, EMC
// Compatibility: BX, EX
static constexpr const std : : pair < const char * , PDFPageContentProcessor : : Operator > operators [ ] =
{
// General graphic state w, J, j, M, d, ri, i, gs
{ " w " , PDFPageContentProcessor : : Operator : : SetLineWidth } ,
{ " J " , PDFPageContentProcessor : : Operator : : SetLineCap } ,
{ " j " , PDFPageContentProcessor : : Operator : : SetLineJoin } ,
{ " M " , PDFPageContentProcessor : : Operator : : SetMitterLimit } ,
{ " d " , PDFPageContentProcessor : : Operator : : SetLineDashPattern } ,
{ " ri " , PDFPageContentProcessor : : Operator : : SetRenderingIntent } ,
{ " i " , PDFPageContentProcessor : : Operator : : SetFlatness } ,
{ " gs " , PDFPageContentProcessor : : Operator : : SetGraphicState } ,
// Special graphic state: q, Q, cm
{ " q " , PDFPageContentProcessor : : Operator : : SaveGraphicState } ,
{ " Q " , PDFPageContentProcessor : : Operator : : RestoreGraphicState } ,
{ " cm " , PDFPageContentProcessor : : Operator : : AdjustCurrentTransformationMatrix } ,
// Path construction: m, l, c, v, y, h, re
{ " m " , PDFPageContentProcessor : : Operator : : MoveCurrentPoint } ,
{ " l " , PDFPageContentProcessor : : Operator : : LineTo } ,
{ " c " , PDFPageContentProcessor : : Operator : : Bezier123To } ,
{ " v " , PDFPageContentProcessor : : Operator : : Bezier23To } ,
{ " y " , PDFPageContentProcessor : : Operator : : Bezier13To } ,
{ " h " , PDFPageContentProcessor : : Operator : : EndSubpath } ,
{ " re " , PDFPageContentProcessor : : Operator : : Rectangle } ,
// Path painting: S, s, f, F, f*, B, B*, b, b*, n
{ " S " , PDFPageContentProcessor : : Operator : : PathStroke } ,
{ " s " , PDFPageContentProcessor : : Operator : : PathCloseStroke } ,
{ " f " , PDFPageContentProcessor : : Operator : : PathFillWinding } ,
{ " F " , PDFPageContentProcessor : : Operator : : PathFillWinding2 } ,
{ " f* " , PDFPageContentProcessor : : Operator : : PathFillEvenOdd } ,
{ " B " , PDFPageContentProcessor : : Operator : : PathFillStrokeWinding } ,
{ " B* " , PDFPageContentProcessor : : Operator : : PathFillStrokeEvenOdd } ,
{ " b " , PDFPageContentProcessor : : Operator : : PathCloseFillStrokeWinding } ,
{ " b* " , PDFPageContentProcessor : : Operator : : PathCloseFillStrokeEvenOdd } ,
{ " n " , PDFPageContentProcessor : : Operator : : PathClear } ,
// Clipping paths: W, W*
{ " W " , PDFPageContentProcessor : : Operator : : ClipWinding } ,
{ " W* " , PDFPageContentProcessor : : Operator : : ClipEvenOdd } ,
// Text object: BT, ET
{ " BT " , PDFPageContentProcessor : : Operator : : TextBegin } ,
{ " ET " , PDFPageContentProcessor : : Operator : : TextEnd } ,
// Text state: Tc, Tw, Tz, TL, Tf, Tr, Ts
{ " Tc " , PDFPageContentProcessor : : Operator : : TextSetCharacterSpacing } ,
{ " Tw " , PDFPageContentProcessor : : Operator : : TextSetWordSpacing } ,
{ " Tz " , PDFPageContentProcessor : : Operator : : TextSetHorizontalScale } ,
{ " TL " , PDFPageContentProcessor : : Operator : : TextSetLeading } ,
{ " Tf " , PDFPageContentProcessor : : Operator : : TextSetFontAndFontSize } ,
{ " Tr " , PDFPageContentProcessor : : Operator : : TextSetRenderMode } ,
{ " Ts " , PDFPageContentProcessor : : Operator : : TextSetRise } ,
// Text positioning: Td, TD, Tm, T*
{ " Td " , PDFPageContentProcessor : : Operator : : TextMoveByOffset } ,
{ " TD " , PDFPageContentProcessor : : Operator : : TextSetLeadingAndMoveByOffset } ,
{ " Tm " , PDFPageContentProcessor : : Operator : : TextSetMatrix } ,
{ " T* " , PDFPageContentProcessor : : Operator : : TextMoveByLeading } ,
// Text showing: Tj, TJ, ', "
{ " Tj " , PDFPageContentProcessor : : Operator : : TextShowTextString } ,
{ " TJ " , PDFPageContentProcessor : : Operator : : TextShowTextIndividualSpacing } ,
{ " ' " , PDFPageContentProcessor : : Operator : : TextNextLineShowText } ,
{ " \" " , PDFPageContentProcessor : : Operator : : TextSetSpacingAndShowText } ,
// Type 3 font: d0, d1
{ " d0 " , PDFPageContentProcessor : : Operator : : Type3FontSetOffset } ,
{ " d1 " , PDFPageContentProcessor : : Operator : : Type3FontSetOffsetAndBB } ,
// Color: CS, cs, SC, SCN, sc, scn, G, g, RG, rg, K, k
{ " CS " , PDFPageContentProcessor : : Operator : : ColorSetStrokingColorSpace } ,
{ " cs " , PDFPageContentProcessor : : Operator : : ColorSetFillingColorSpace } ,
{ " SC " , PDFPageContentProcessor : : Operator : : ColorSetStrokingColor } ,
{ " SCN " , PDFPageContentProcessor : : Operator : : ColorSetStrokingColorN } ,
{ " sc " , PDFPageContentProcessor : : Operator : : ColorSetFillingColor } ,
{ " scn " , PDFPageContentProcessor : : Operator : : ColorSetFillingColorN } ,
{ " G " , PDFPageContentProcessor : : Operator : : ColorSetDeviceGrayStroking } ,
{ " g " , PDFPageContentProcessor : : Operator : : ColorSetDeviceGrayFilling } ,
{ " RG " , PDFPageContentProcessor : : Operator : : ColorSetDeviceRGBStroking } ,
{ " rg " , PDFPageContentProcessor : : Operator : : ColorSetDeviceRGBFilling } ,
{ " K " , PDFPageContentProcessor : : Operator : : ColorSetDeviceCMYKStroking } ,
{ " k " , PDFPageContentProcessor : : Operator : : ColorSetDeviceCMYKFilling } ,
// Shading pattern: sh
{ " sh " , PDFPageContentProcessor : : Operator : : ShadingPaintShape } ,
// Inline images: BI, ID, EI
{ " BI " , PDFPageContentProcessor : : Operator : : InlineImageBegin } ,
{ " ID " , PDFPageContentProcessor : : Operator : : InlineImageData } ,
{ " EI " , PDFPageContentProcessor : : Operator : : InlineImageEnd } ,
// XObject: Do
{ " Do " , PDFPageContentProcessor : : Operator : : PaintXObject } ,
// Marked content: MP, DP, BMC, BDC, EMC
{ " MP " , PDFPageContentProcessor : : Operator : : MarkedContentPoint } ,
{ " DP " , PDFPageContentProcessor : : Operator : : MarkedContentPointWithProperties } ,
{ " BMC " , PDFPageContentProcessor : : Operator : : MarkedContentBegin } ,
{ " BDC " , PDFPageContentProcessor : : Operator : : MarkedContentBeginWithProperties } ,
{ " EMC " , PDFPageContentProcessor : : Operator : : MarkedContentEnd } ,
// Compatibility: BX, EX
{ " BX " , PDFPageContentProcessor : : Operator : : CompatibilityBegin } ,
{ " EX " , PDFPageContentProcessor : : Operator : : CompatibilityEnd }
} ;
2019-06-16 16:32:23 +02:00
void PDFPageContentProcessor : : initDictionaries ( const PDFObject & resourcesObject )
2019-02-24 17:48:37 +01:00
{
2019-06-16 16:32:23 +02:00
auto getDictionary = [ this , & resourcesObject ] ( const char * resourceName ) - > const pdf : : PDFDictionary *
2019-02-24 17:48:37 +01:00
{
2019-06-16 16:32:23 +02:00
const PDFObject & resources = m_document - > getObject ( resourcesObject ) ;
2019-03-30 18:45:30 +01:00
if ( resources . isDictionary ( ) & & resources . getDictionary ( ) - > hasKey ( resourceName ) )
2019-02-24 17:48:37 +01:00
{
2019-03-30 18:45:30 +01:00
const PDFObject & resourceDictionary = m_document - > getObject ( resources . getDictionary ( ) - > get ( resourceName ) ) ;
if ( resourceDictionary . isDictionary ( ) )
{
return resourceDictionary . getDictionary ( ) ;
}
2019-02-24 17:48:37 +01:00
}
2019-03-30 18:45:30 +01:00
return nullptr ;
} ;
m_colorSpaceDictionary = getDictionary ( COLOR_SPACE_DICTIONARY ) ;
m_fontDictionary = getDictionary ( " Font " ) ;
2019-05-07 18:21:22 +02:00
m_xobjectDictionary = getDictionary ( " XObject " ) ;
2019-06-16 16:32:23 +02:00
m_extendedGraphicStateDictionary = getDictionary ( PDF_RESOURCE_EXTGSTATE ) ;
2019-07-04 17:52:38 +02:00
m_propertiesDictionary = getDictionary ( " Properties " ) ;
2019-08-25 18:16:37 +02:00
m_shadingDictionary = getDictionary ( " Shading " ) ;
2019-09-13 16:28:20 +02:00
m_patternDictionary = getDictionary ( " Pattern " ) ;
2019-06-16 16:32:23 +02:00
}
2019-07-04 17:52:38 +02:00
PDFPageContentProcessor : : PDFPageContentProcessor ( const PDFPage * page ,
const PDFDocument * document ,
const PDFFontCache * fontCache ,
2019-12-25 17:56:17 +01:00
const PDFCMS * CMS ,
2019-08-25 18:16:37 +02:00
const PDFOptionalContentActivity * optionalContentActivity ,
2019-09-28 18:26:31 +02:00
QMatrix pagePointToDevicePointMatrix ,
const PDFMeshQualitySettings & meshQualitySettings ) :
2019-06-16 16:32:23 +02:00
m_page ( page ) ,
m_document ( document ) ,
m_fontCache ( fontCache ) ,
2019-12-25 17:56:17 +01:00
m_CMS ( CMS ) ,
2019-07-04 17:52:38 +02:00
m_optionalContentActivity ( optionalContentActivity ) ,
2019-06-16 16:32:23 +02:00
m_colorSpaceDictionary ( nullptr ) ,
m_fontDictionary ( nullptr ) ,
m_xobjectDictionary ( nullptr ) ,
m_extendedGraphicStateDictionary ( nullptr ) ,
2019-07-04 17:52:38 +02:00
m_propertiesDictionary ( nullptr ) ,
2019-08-25 18:16:37 +02:00
m_shadingDictionary ( nullptr ) ,
2019-09-13 16:28:20 +02:00
m_patternDictionary ( nullptr ) ,
2019-06-23 18:35:32 +02:00
m_textBeginEndState ( 0 ) ,
2019-08-25 18:16:37 +02:00
m_compatibilityBeginEndState ( 0 ) ,
2019-09-27 18:41:56 +02:00
m_drawingUncoloredTilingPatternState ( 0 ) ,
2019-08-31 14:37:18 +02:00
m_patternBaseMatrix ( pagePointToDevicePointMatrix ) ,
2019-09-28 18:26:31 +02:00
m_pagePointToDevicePointMatrix ( pagePointToDevicePointMatrix ) ,
m_meshQualitySettings ( meshQualitySettings )
2019-06-16 16:32:23 +02:00
{
Q_ASSERT ( page ) ;
Q_ASSERT ( document ) ;
2020-01-18 14:55:26 +01:00
PDFExecutionPolicy : : startProcessingContentStream ( ) ;
2019-08-25 18:16:37 +02:00
QPainterPath pageRectPath ;
pageRectPath . addRect ( m_page - > getRotatedMediaBox ( ) ) ;
2019-08-31 14:37:18 +02:00
m_pageBoundingRectDeviceSpace = pagePointToDevicePointMatrix . map ( pageRectPath ) . boundingRect ( ) ;
2019-08-25 18:16:37 +02:00
2019-06-16 16:32:23 +02:00
initDictionaries ( m_page - > getResources ( ) ) ;
2019-02-24 17:48:37 +01:00
}
PDFPageContentProcessor : : ~ PDFPageContentProcessor ( )
{
2020-01-18 14:55:26 +01:00
PDFExecutionPolicy : : endProcessingContentStream ( ) ;
2019-02-24 17:48:37 +01:00
}
QList < PDFRenderError > PDFPageContentProcessor : : processContents ( )
{
const PDFObject & contents = m_page - > getContents ( ) ;
// Clear the old errors
m_errorList . clear ( ) ;
// Initialize default color spaces (gray, RGB, CMYK)
try
{
m_deviceGrayColorSpace = PDFAbstractColorSpace : : createDeviceColorSpaceByName ( m_colorSpaceDictionary , m_document , COLOR_SPACE_NAME_DEVICE_GRAY ) ;
m_deviceRGBColorSpace = PDFAbstractColorSpace : : createDeviceColorSpaceByName ( m_colorSpaceDictionary , m_document , COLOR_SPACE_NAME_DEVICE_RGB ) ;
m_deviceCMYKColorSpace = PDFAbstractColorSpace : : createDeviceColorSpaceByName ( m_colorSpaceDictionary , m_document , COLOR_SPACE_NAME_DEVICE_CMYK ) ;
}
2019-09-27 18:41:56 +02:00
catch ( PDFException exception )
2019-02-24 17:48:37 +01:00
{
m_errorList . append ( PDFRenderError ( RenderErrorType : : Error , exception . getMessage ( ) ) ) ;
// Create default color spaces anyway, but do not try to load them...
m_deviceGrayColorSpace . reset ( new PDFDeviceGrayColorSpace ) ;
m_deviceRGBColorSpace . reset ( new PDFDeviceRGBColorSpace ) ;
m_deviceCMYKColorSpace . reset ( new PDFDeviceCMYKColorSpace ) ;
}
m_graphicState . setStrokeColorSpace ( m_deviceGrayColorSpace ) ;
2019-12-25 17:56:17 +01:00
m_graphicState . setStrokeColor ( m_deviceGrayColorSpace - > getDefaultColor ( m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ) ;
2019-02-24 17:48:37 +01:00
m_graphicState . setFillColorSpace ( m_deviceGrayColorSpace ) ;
2019-12-25 17:56:17 +01:00
m_graphicState . setFillColor ( m_deviceGrayColorSpace - > getDefaultColor ( m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ) ;
2019-02-24 17:48:37 +01:00
m_graphicState . setStateFlags ( PDFPageContentProcessorState : : StateAll ) ;
updateGraphicState ( ) ;
if ( contents . isArray ( ) )
{
const PDFArray * array = contents . getArray ( ) ;
const size_t count = array - > getCount ( ) ;
for ( size_t i = 0 ; i < count ; + + i )
{
const PDFObject & streamObject = m_document - > getObject ( array - > getItem ( i ) ) ;
if ( streamObject . isStream ( ) )
{
processContentStream ( streamObject . getStream ( ) ) ;
}
else
{
m_errorList . append ( PDFRenderError ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid page contents. " ) ) ) ;
}
}
}
else if ( contents . isStream ( ) )
{
processContentStream ( contents . getStream ( ) ) ;
}
else
{
m_errorList . append ( PDFRenderError ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid page contents. " ) ) ) ;
}
if ( ! m_stack . empty ( ) )
{
// Stack is not empty. There was more saves than restores. This is error.
m_errorList . append ( PDFRenderError ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Graphic state stack was saved more times, than was restored. " ) ) ) ;
while ( ! m_stack . empty ( ) )
{
operatorRestoreGraphicState ( ) ;
}
}
return m_errorList ;
}
2019-04-30 14:39:48 +02:00
void PDFPageContentProcessor : : reportRenderError ( RenderErrorType type , QString message )
{
m_errorList . append ( PDFRenderError ( type , qMove ( message ) ) ) ;
}
2019-05-04 18:22:40 +02:00
void PDFPageContentProcessor : : performPathPainting ( const QPainterPath & path , bool stroke , bool fill , bool text , Qt : : FillRule fillRule )
2019-02-24 17:48:37 +01:00
{
Q_UNUSED ( path ) ;
Q_UNUSED ( stroke ) ;
Q_UNUSED ( fill ) ;
2019-05-04 18:22:40 +02:00
Q_UNUSED ( text ) ;
2019-02-24 17:48:37 +01:00
Q_UNUSED ( fillRule ) ;
}
void PDFPageContentProcessor : : performClipping ( const QPainterPath & path , Qt : : FillRule fillRule )
{
Q_UNUSED ( path ) ;
Q_UNUSED ( fillRule ) ;
}
2019-05-07 18:21:22 +02:00
void PDFPageContentProcessor : : performImagePainting ( const QImage & image )
{
Q_UNUSED ( image ) ;
}
2019-08-31 15:55:59 +02:00
void PDFPageContentProcessor : : performMeshPainting ( const PDFMesh & mesh )
{
Q_UNUSED ( mesh ) ;
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : performUpdateGraphicsState ( const PDFPageContentProcessorState & state )
2019-02-24 17:48:37 +01:00
{
2019-03-30 18:45:30 +01:00
if ( state . getStateFlags ( ) . testFlag ( PDFPageContentProcessorState : : StateTextFont ) | |
state . getStateFlags ( ) . testFlag ( PDFPageContentProcessorState : : StateTextFontSize ) )
{
m_realizedFont . dirty ( ) ;
}
2019-02-24 17:48:37 +01:00
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : performSaveGraphicState ( ProcessOrder order )
2019-02-24 17:48:37 +01:00
{
Q_UNUSED ( order ) ;
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : performRestoreGraphicState ( ProcessOrder order )
2019-02-24 17:48:37 +01:00
{
Q_UNUSED ( order ) ;
}
2019-07-04 17:52:38 +02:00
void PDFPageContentProcessor : : performMarkedContentPoint ( const QByteArray & tag , const PDFObject & properties )
{
Q_UNUSED ( tag ) ;
Q_UNUSED ( properties ) ;
}
void PDFPageContentProcessor : : performMarkedContentBegin ( const QByteArray & tag , const PDFObject & properties )
{
Q_UNUSED ( tag ) ;
Q_UNUSED ( properties ) ;
}
void PDFPageContentProcessor : : performMarkedContentEnd ( )
{
}
2019-07-21 17:31:39 +02:00
void PDFPageContentProcessor : : performSetCharWidth ( PDFReal wx , PDFReal wy )
{
Q_UNUSED ( wx ) ;
Q_UNUSED ( wy ) ;
}
void PDFPageContentProcessor : : performSetCacheDevice ( PDFReal wx , PDFReal wy , PDFReal llx , PDFReal lly , PDFReal urx , PDFReal ury )
{
Q_UNUSED ( wx ) ;
Q_UNUSED ( wy ) ;
Q_UNUSED ( llx ) ;
Q_UNUSED ( lly ) ;
Q_UNUSED ( urx ) ;
Q_UNUSED ( ury ) ;
}
2019-09-29 18:09:09 +02:00
void PDFPageContentProcessor : : performBeginTransparencyGroup ( ProcessOrder order , const PDFTransparencyGroup & transparencyGroup )
{
Q_UNUSED ( order ) ;
Q_UNUSED ( transparencyGroup ) ;
}
void PDFPageContentProcessor : : performEndTransparencyGroup ( ProcessOrder order , const PDFTransparencyGroup & transparencyGroup )
{
Q_UNUSED ( order ) ;
Q_UNUSED ( transparencyGroup ) ;
}
2019-12-28 19:21:29 +01:00
void PDFPageContentProcessor : : performOutputCharacter ( const PDFTextCharacterInfo & info )
{
Q_UNUSED ( info ) ;
}
2019-12-31 17:39:31 +01:00
bool PDFPageContentProcessor : : isContentKindSuppressed ( PDFPageContentProcessor : : ContentKind kind ) const
{
Q_UNUSED ( kind ) ;
return false ;
}
2019-07-04 17:52:38 +02:00
bool PDFPageContentProcessor : : isContentSuppressed ( ) const
{
return std : : any_of ( m_markedContentStack . cbegin ( ) , m_markedContentStack . cend ( ) , [ ] ( const MarkedContentState & state ) { return state . contentSuppressed ; } ) ;
}
2019-06-16 16:32:23 +02:00
void PDFPageContentProcessor : : processContent ( const QByteArray & content )
2019-02-24 17:48:37 +01:00
{
PDFLexicalAnalyzer parser ( content . constBegin ( ) , content . constEnd ( ) ) ;
while ( ! parser . isAtEnd ( ) )
{
try
{
PDFLexicalAnalyzer : : Token token = parser . fetch ( ) ;
switch ( token . type )
{
case PDFLexicalAnalyzer : : TokenType : : Command :
{
2019-07-21 17:31:39 +02:00
QByteArray command = token . data . toByteArray ( ) ;
if ( command = = " BI " )
{
// Strategy: We will try to find position of BI/ID/EI in the stream. If we can determine
// length of the stream explicitly, then we use explicit length. We also create a PDFObject
// from the inline image dictionary/image content stream and then process it like XObject.
PDFInteger operatorBIPosition = parser . pos ( ) ;
PDFInteger operatorIDPosition = parser . findSubstring ( " ID " , operatorBIPosition ) ;
PDFInteger operatorEIPosition = parser . findSubstring ( " EI " , operatorIDPosition ) ;
// According the PDF 1.7 specification, single white space characters is after ID, then the byte
// immediately after it is interpreted as first byte of image data.
PDFInteger startDataPosition = operatorIDPosition + 3 ;
if ( operatorIDPosition = = - 1 | | operatorEIPosition = = - 1 )
{
2019-09-27 18:41:56 +02:00
throw PDFException ( PDFTranslationContext : : tr ( " Invalid inline image dictionary, ID operator is missing. " ) ) ;
2019-07-21 17:31:39 +02:00
}
Q_ASSERT ( operatorBIPosition < content . size ( ) ) ;
Q_ASSERT ( operatorIDPosition < content . size ( ) ) ;
Q_ASSERT ( operatorBIPosition < = operatorIDPosition ) ;
PDFLexicalAnalyzer inlineImageLexicalAnalyzer ( content . constBegin ( ) + operatorBIPosition , content . constBegin ( ) + operatorIDPosition ) ;
PDFParser inlineImageParser ( [ & inlineImageLexicalAnalyzer ] { return inlineImageLexicalAnalyzer . fetch ( ) ; } ) ;
constexpr std : : pair < const char * , const char * > replacements [ ] =
{
{ " BPC " , " BitsPerComponent " } ,
{ " CS " , " ColorSpace " } ,
{ " D " , " Decode " } ,
{ " DP " , " DecodeParms " } ,
{ " F " , " Filter " } ,
{ " H " , " Height " } ,
{ " IM " , " ImageMask " } ,
{ " I " , " Interpolate " } ,
{ " W " , " Width " }
} ;
std : : shared_ptr < PDFDictionary > dictionarySharedPointer = std : : make_shared < PDFDictionary > ( ) ;
PDFDictionary * dictionary = dictionarySharedPointer . get ( ) ;
while ( inlineImageParser . lookahead ( ) . type ! = PDFLexicalAnalyzer : : TokenType : : EndOfFile )
{
PDFObject nameObject = inlineImageParser . getObject ( ) ;
PDFObject valueObject = inlineImageParser . getObject ( ) ;
if ( ! nameObject . isName ( ) )
{
2019-09-27 18:41:56 +02:00
throw PDFException ( PDFTranslationContext : : tr ( " Expected name in the inline image dictionary stream. " ) ) ;
2019-07-21 17:31:39 +02:00
}
// Replace the name, if neccessary
QByteArray name = nameObject . getString ( ) ;
for ( auto [ string , replacement ] : replacements )
{
if ( name = = string )
{
name = replacement ;
break ;
}
}
dictionary - > addEntry ( qMove ( name ) , qMove ( valueObject ) ) ;
}
PDFDocumentDataLoaderDecorator loader ( m_document ) ;
PDFInteger dataLength = 0 ;
if ( dictionary - > hasKey ( " Length " ) )
{
dataLength = loader . readIntegerFromDictionary ( dictionary , " Length " , 0 ) ;
}
else if ( dictionary - > hasKey ( " Filter " ) )
{
// We will use EI operator position to determine stream length
dataLength = operatorEIPosition - startDataPosition ;
}
else
{
// We will calculate stream size from the with/height and bit per component
const PDFInteger width = loader . readIntegerFromDictionary ( dictionary , " Width " , 0 ) ;
const PDFInteger height = loader . readIntegerFromDictionary ( dictionary , " Height " , 0 ) ;
const PDFInteger bpc = loader . readIntegerFromDictionary ( dictionary , " BitsPerComponent " , 8 ) ;
if ( width < = 0 | | height < = 0 | | bpc < = 0 )
{
2019-09-27 18:41:56 +02:00
throw PDFException ( PDFTranslationContext : : tr ( " Expected name in the inline image dictionary stream. " ) ) ;
2019-07-21 17:31:39 +02:00
}
const PDFInteger stride = ( width * bpc + 7 ) / 8 ;
dataLength = stride * height ;
}
// We will once more find the "EI" operator, due to recomputed dataLength.
operatorEIPosition = parser . findSubstring ( " EI " , startDataPosition + dataLength ) ;
if ( operatorEIPosition = = - 1 )
{
2019-09-27 18:41:56 +02:00
throw PDFException ( PDFTranslationContext : : tr ( " Invalid inline image stream. " ) ) ;
2019-07-21 17:31:39 +02:00
}
// We must seek after EI operator. Then we will paint the image. Because painting of image can throw exception,
// then we will paint the image AFTER we seek the position.
parser . seek ( operatorEIPosition + 2 ) ;
QByteArray buffer = content . mid ( startDataPosition , dataLength ) ;
PDFStream imageStream ( std : : move ( * dictionary ) , std : : move ( buffer ) ) ;
paintXObjectImage ( & imageStream ) ;
}
else
{
// Process the command, then clear the operand stack
processCommand ( command ) ;
}
2019-02-24 17:48:37 +01:00
m_operands . clear ( ) ;
break ;
}
case PDFLexicalAnalyzer : : TokenType : : EndOfFile :
{
// Do nothing, just break, we are at the end
break ;
}
default :
{
// Push the operand onto the operand stack
m_operands . push_back ( std : : move ( token ) ) ;
break ;
}
}
}
2019-09-27 18:41:56 +02:00
catch ( PDFException exception )
2019-02-24 17:48:37 +01:00
{
2019-03-17 16:12:36 +01:00
m_operands . clear ( ) ;
2019-02-24 17:48:37 +01:00
m_errorList . append ( PDFRenderError ( RenderErrorType : : Error , exception . getMessage ( ) ) ) ;
}
catch ( PDFRendererException exception )
{
2019-03-17 16:12:36 +01:00
m_operands . clear ( ) ;
2019-02-24 17:48:37 +01:00
m_errorList . append ( exception . getError ( ) ) ;
}
}
}
2019-06-16 16:32:23 +02:00
void PDFPageContentProcessor : : processContentStream ( const PDFStream * stream )
{
2019-08-18 16:03:41 +02:00
try
{
QByteArray content = m_document - > getDecodedStream ( stream ) ;
2019-06-16 16:32:23 +02:00
2019-08-18 16:03:41 +02:00
processContent ( content ) ;
}
2019-09-27 18:41:56 +02:00
catch ( PDFException exception )
2019-08-18 16:03:41 +02:00
{
m_operands . clear ( ) ;
m_errorList . append ( PDFRenderError ( RenderErrorType : : Error , exception . getMessage ( ) ) ) ;
}
2019-06-16 16:32:23 +02:00
}
2019-09-29 18:09:09 +02:00
void PDFPageContentProcessor : : processForm ( const QMatrix & matrix ,
const QRectF & boundingBox ,
const PDFObject & resources ,
const PDFObject & transparencyGroup ,
const QByteArray & content )
2019-06-16 16:32:23 +02:00
{
PDFPageContentProcessorStateGuard guard ( this ) ;
2019-09-29 18:09:09 +02:00
std : : unique_ptr < PDFTransparencyGroupGuard > guard2 ;
if ( transparencyGroup . isDictionary ( ) )
{
// Parse the transparency group
const PDFDictionary * transparencyDictionary = transparencyGroup . getDictionary ( ) ;
PDFDocumentDataLoaderDecorator loader ( m_document ) ;
PDFTransparencyGroup group ;
const PDFObject & colorSpaceObject = m_document - > getObject ( transparencyDictionary - > get ( " CS " ) ) ;
if ( ! colorSpaceObject . isNull ( ) )
{
group . colorSpacePointer = PDFAbstractColorSpace : : createColorSpace ( m_colorSpaceDictionary , m_document , colorSpaceObject ) ;
}
group . isolated = loader . readBooleanFromDictionary ( transparencyDictionary , " I " , false ) ;
group . knockout = loader . readBooleanFromDictionary ( transparencyDictionary , " K " , false ) ;
guard2 . reset ( new PDFTransparencyGroupGuard ( this , qMove ( group ) ) ) ;
// If we are in transparency group, we must reset transparency settings in the graphic state.
// Graphic state is then updated in the lines below.
m_graphicState . setBlendMode ( BlendMode : : Normal ) ;
m_graphicState . setAlphaFilling ( 1.0 ) ;
m_graphicState . setAlphaStroking ( 1.0 ) ;
}
2019-06-16 16:32:23 +02:00
QMatrix formMatrix = matrix * m_graphicState . getCurrentTransformationMatrix ( ) ;
m_graphicState . setCurrentTransformationMatrix ( formMatrix ) ;
updateGraphicState ( ) ;
2019-08-31 14:37:18 +02:00
QMatrix patternMatrix = formMatrix * m_pagePointToDevicePointMatrix ;
PDFTemporaryValueChange patternMatrixGuard ( & m_patternBaseMatrix , patternMatrix ) ;
2019-08-25 18:16:37 +02:00
2019-06-16 16:32:23 +02:00
// If the clipping box is valid, then use clipping. Clipping box is in the form coordinate system
if ( boundingBox . isValid ( ) )
{
QPainterPath path ;
path . addRect ( boundingBox ) ;
performClipping ( path , path . fillRule ( ) ) ;
}
// Initialize the resources, if we have them
if ( ! resources . isNull ( ) )
{
initDictionaries ( resources ) ;
}
processContent ( content ) ;
}
2019-08-31 15:55:59 +02:00
void PDFPageContentProcessor : : processPathPainting ( const QPainterPath & path , bool stroke , bool fill , bool text , Qt : : FillRule fillRule )
{
2019-12-31 17:39:31 +01:00
if ( isContentSuppressed ( ) | | ( text & & isContentKindSuppressed ( ContentKind : : Text ) ) | | ( ! text & & isContentKindSuppressed ( ContentKind : : Shapes ) ) )
2019-08-31 15:55:59 +02:00
{
// Content is suppressed, do not paint anything
return ;
}
if ( ( ! stroke & & ! fill ) | | path . isEmpty ( ) )
{
// No operation requested - either path is empty, or neither stroking or filling
return ;
}
if ( fill )
{
2019-09-26 19:14:04 +02:00
if ( const PDFPatternColorSpace * patternColorSpace = getGraphicState ( ) - > getFillColorSpace ( ) - > asPatternColorSpace ( ) )
2019-08-31 15:55:59 +02:00
{
2019-09-26 19:14:04 +02:00
const PDFPattern * pattern = patternColorSpace - > getPattern ( ) ;
2019-09-13 16:28:20 +02:00
switch ( pattern - > getType ( ) )
2019-08-31 15:55:59 +02:00
{
2019-09-13 16:28:20 +02:00
case PatternType : : Tiling :
{
2019-12-31 17:39:31 +01:00
if ( ! isContentKindSuppressed ( ContentKind : : Tiling ) )
{
// Tiling is enabled
const PDFTilingPattern * tilingPattern = pattern - > getTilingPattern ( ) ;
processTillingPatternPainting ( tilingPattern , path , patternColorSpace - > getUncoloredPatternColorSpace ( ) , patternColorSpace - > getUncoloredPatternColor ( ) ) ;
}
2019-09-13 16:28:20 +02:00
break ;
}
2019-08-31 15:55:59 +02:00
2019-09-13 16:28:20 +02:00
case PatternType : : Shading :
{
2019-12-31 17:39:31 +01:00
if ( ! isContentKindSuppressed ( ContentKind : : Shading ) )
2019-09-27 18:41:56 +02:00
{
2019-12-31 17:39:31 +01:00
// Shading is enabled
PDFPageContentProcessorGraphicStateSaveRestoreGuard guard ( this ) ;
const PDFShadingPattern * shadingPattern = pattern - > getShadingPattern ( ) ;
// Apply pattern graphic state
const PDFObject & patternGraphicState = m_document - > getObject ( shadingPattern - > getPatternGraphicState ( ) ) ;
if ( ! patternGraphicState . isNull ( ) )
{
if ( patternGraphicState . isDictionary ( ) )
{
processApplyGraphicState ( patternGraphicState . getDictionary ( ) ) ;
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Shading pattern graphic state is invalid. " ) ) ;
}
}
// We must create a mesh and then draw pattern
PDFMeshQualitySettings settings = m_meshQualitySettings ;
settings . deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace ( ) ;
settings . userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix ( ) ;
settings . initResolution ( ) ;
PDFMesh mesh = shadingPattern - > createMesh ( settings , m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ;
// Now, merge the current path to the mesh clipping path
QPainterPath boundingPath = mesh . getBoundingPath ( ) ;
if ( boundingPath . isEmpty ( ) )
2019-09-27 18:41:56 +02:00
{
2019-12-31 17:39:31 +01:00
boundingPath = getCurrentWorldMatrix ( ) . map ( path ) ;
2019-09-27 18:41:56 +02:00
}
else
{
2019-12-31 17:39:31 +01:00
boundingPath = boundingPath . intersected ( path ) ;
2019-09-27 18:41:56 +02:00
}
2019-12-31 17:39:31 +01:00
mesh . setBoundingPath ( boundingPath ) ;
2019-08-31 15:55:59 +02:00
2019-12-31 17:39:31 +01:00
performMeshPainting ( mesh ) ;
2019-09-21 15:55:33 +02:00
}
2019-09-13 16:28:20 +02:00
break ;
}
case PatternType : : Invalid :
{
2019-09-27 18:41:56 +02:00
throw PDFException ( PDFTranslationContext : : tr ( " Invalid pattern. " ) ) ;
2019-09-13 16:28:20 +02:00
break ;
}
default :
{
Q_ASSERT ( false ) ;
break ;
}
2019-08-31 15:55:59 +02:00
}
fill = false ;
}
}
if ( stroke )
{
2019-09-26 19:14:04 +02:00
if ( const PDFPatternColorSpace * patternColorSpace = getGraphicState ( ) - > getFillColorSpace ( ) - > asPatternColorSpace ( ) )
2019-08-31 15:55:59 +02:00
{
2019-09-26 19:14:04 +02:00
const PDFPattern * pattern = patternColorSpace - > getPattern ( ) ;
2019-09-13 16:28:20 +02:00
switch ( pattern - > getType ( ) )
2019-08-31 15:55:59 +02:00
{
2019-09-13 16:28:20 +02:00
case PatternType : : Tiling :
2019-08-31 15:55:59 +02:00
{
2019-12-31 17:39:31 +01:00
if ( ! isContentKindSuppressed ( ContentKind : : Tiling ) )
2019-09-25 19:17:52 +02:00
{
2019-12-31 17:39:31 +01:00
// Tiling is enabled
const PDFTilingPattern * tilingPattern = pattern - > getTilingPattern ( ) ;
// We must stroke the path.
QPainterPathStroker stroker ;
stroker . setCapStyle ( m_graphicState . getLineCapStyle ( ) ) ;
stroker . setWidth ( m_graphicState . getLineWidth ( ) ) ;
stroker . setMiterLimit ( m_graphicState . getMitterLimit ( ) ) ;
stroker . setJoinStyle ( m_graphicState . getLineJoinStyle ( ) ) ;
const PDFLineDashPattern & lineDashPattern = m_graphicState . getLineDashPattern ( ) ;
if ( ! lineDashPattern . isSolid ( ) )
{
stroker . setDashPattern ( QVector < PDFReal > : : fromStdVector ( lineDashPattern . getDashArray ( ) ) ) ;
stroker . setDashOffset ( lineDashPattern . getDashOffset ( ) ) ;
}
QPainterPath strokedPath = stroker . createStroke ( path ) ;
processTillingPatternPainting ( tilingPattern , strokedPath , patternColorSpace - > getUncoloredPatternColorSpace ( ) , patternColorSpace - > getUncoloredPatternColor ( ) ) ;
2019-09-25 19:17:52 +02:00
}
2019-09-13 16:28:20 +02:00
break ;
2019-08-31 15:55:59 +02:00
}
2019-09-13 16:28:20 +02:00
case PatternType : : Shading :
{
2019-12-31 17:39:31 +01:00
if ( ! isContentKindSuppressed ( ContentKind : : Shading ) )
2019-09-27 18:41:56 +02:00
{
2019-12-31 17:39:31 +01:00
// Shading is enabled
PDFPageContentProcessorGraphicStateSaveRestoreGuard guard ( this ) ;
const PDFShadingPattern * shadingPattern = pattern - > getShadingPattern ( ) ;
// Apply pattern graphic state
const PDFObject & patternGraphicState = m_document - > getObject ( shadingPattern - > getPatternGraphicState ( ) ) ;
if ( ! patternGraphicState . isNull ( ) )
2019-09-27 18:41:56 +02:00
{
2019-12-31 17:39:31 +01:00
if ( patternGraphicState . isDictionary ( ) )
{
processApplyGraphicState ( patternGraphicState . getDictionary ( ) ) ;
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Shading pattern graphic state is invalid. " ) ) ;
}
2019-09-27 18:41:56 +02:00
}
2019-08-31 15:55:59 +02:00
2019-12-31 17:39:31 +01:00
// We must create a mesh and then draw pattern
PDFMeshQualitySettings settings = m_meshQualitySettings ;
settings . deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace ( ) ;
settings . userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix ( ) ;
settings . initResolution ( ) ;
2019-09-13 16:28:20 +02:00
2019-12-31 17:39:31 +01:00
PDFMesh mesh = shadingPattern - > createMesh ( settings , m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ;
2019-09-13 16:28:20 +02:00
2019-12-31 17:39:31 +01:00
// We must stroke the path.
QPainterPathStroker stroker ;
stroker . setCapStyle ( m_graphicState . getLineCapStyle ( ) ) ;
stroker . setWidth ( m_graphicState . getLineWidth ( ) ) ;
stroker . setMiterLimit ( m_graphicState . getMitterLimit ( ) ) ;
stroker . setJoinStyle ( m_graphicState . getLineJoinStyle ( ) ) ;
2019-09-13 16:28:20 +02:00
2019-12-31 17:39:31 +01:00
const PDFLineDashPattern & lineDashPattern = m_graphicState . getLineDashPattern ( ) ;
if ( ! lineDashPattern . isSolid ( ) )
{
stroker . setDashPattern ( QVector < PDFReal > : : fromStdVector ( lineDashPattern . getDashArray ( ) ) ) ;
stroker . setDashOffset ( lineDashPattern . getDashOffset ( ) ) ;
}
QPainterPath strokedPath = stroker . createStroke ( path ) ;
2019-09-13 16:28:20 +02:00
2019-12-31 17:39:31 +01:00
QPainterPath boundingPath = mesh . getBoundingPath ( ) ;
if ( boundingPath . isEmpty ( ) )
{
boundingPath = getCurrentWorldMatrix ( ) . map ( strokedPath ) ;
}
else
{
boundingPath = boundingPath . intersected ( strokedPath ) ;
}
mesh . setBoundingPath ( boundingPath ) ;
2019-09-13 16:28:20 +02:00
2019-12-31 17:39:31 +01:00
performMeshPainting ( mesh ) ;
}
2019-09-13 16:28:20 +02:00
break ;
}
case PatternType : : Invalid :
{
2019-09-27 18:41:56 +02:00
throw PDFException ( PDFTranslationContext : : tr ( " Invalid pattern. " ) ) ;
2019-09-13 16:28:20 +02:00
break ;
}
default :
{
Q_ASSERT ( false ) ;
break ;
}
2019-08-31 15:55:59 +02:00
}
stroke = false ;
}
}
if ( stroke | | fill )
{
performPathPainting ( path , stroke , fill , text , fillRule ) ;
}
}
2019-09-26 19:14:04 +02:00
void PDFPageContentProcessor : : processTillingPatternPainting ( const PDFTilingPattern * tilingPattern ,
const QPainterPath & path ,
PDFColorSpacePointer uncoloredPatternColorSpace ,
PDFColor uncoloredPatternColor )
2019-09-25 19:17:52 +02:00
{
PDFPageContentProcessorStateGuard guard ( this ) ;
performClipping ( path , path . fillRule ( ) ) ;
// Initialize resources
const PDFObject & resources = tilingPattern - > getResources ( ) ;
if ( ! resources . isNull ( ) )
{
initDictionaries ( resources ) ;
}
Q_ASSERT ( m_pagePointToDevicePointMatrix . isInvertible ( ) ) ;
// Initialize rendering matrix
QMatrix patternMatrix = tilingPattern - > getMatrix ( ) * getPatternBaseMatrix ( ) ;
QMatrix matrix = patternMatrix * m_pagePointToDevicePointMatrix . inverted ( ) ;
QMatrix pathTransformationMatrix = m_graphicState . getCurrentTransformationMatrix ( ) * matrix . inverted ( ) ;
m_graphicState . setCurrentTransformationMatrix ( matrix ) ;
2019-09-26 19:14:04 +02:00
2019-09-27 18:41:56 +02:00
int uncoloredTilingPatternFlag = 0 ;
2019-09-26 19:14:04 +02:00
// Initialize colors for uncolored color space pattern
if ( tilingPattern - > getPaintingType ( ) = = PDFTilingPattern : : PaintType : : Uncolored )
{
if ( ! uncoloredPatternColorSpace )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Uncolored tiling pattern has not underlying color space. " ) ) ;
}
2019-09-27 18:41:56 +02:00
uncoloredTilingPatternFlag = 1 ;
2019-09-26 19:14:04 +02:00
m_graphicState . setStrokeColorSpace ( uncoloredPatternColorSpace ) ;
m_graphicState . setFillColorSpace ( uncoloredPatternColorSpace ) ;
2019-12-25 17:56:17 +01:00
QColor color = uncoloredPatternColorSpace - > getCheckedColor ( uncoloredPatternColor , m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ;
2019-09-26 19:14:04 +02:00
m_graphicState . setStrokeColor ( color ) ;
m_graphicState . setFillColor ( color ) ;
}
else
{
// Jakub Melka: According the specification, we set default color space and default color
m_graphicState . setStrokeColorSpace ( m_deviceGrayColorSpace ) ;
m_graphicState . setFillColorSpace ( m_deviceGrayColorSpace ) ;
2019-12-25 17:56:17 +01:00
QColor color = m_deviceGrayColorSpace - > getDefaultColor ( m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ;
2019-09-26 19:14:04 +02:00
m_graphicState . setStrokeColor ( color ) ;
m_graphicState . setFillColor ( color ) ;
}
2019-09-25 19:17:52 +02:00
updateGraphicState ( ) ;
2019-09-27 18:41:56 +02:00
// Mark uncolored flag, if we drawing uncolored color pattern
PDFTemporaryValueChange guard2 ( & m_drawingUncoloredTilingPatternState , m_drawingUncoloredTilingPatternState + uncoloredTilingPatternFlag ) ;
2019-09-25 19:17:52 +02:00
// Tiling parameters
const QRectF tilingArea = pathTransformationMatrix . map ( path ) . boundingRect ( ) ;
const QRectF boundingBox = tilingPattern - > getBoundingBox ( ) ;
const PDFReal xStep = qAbs ( tilingPattern - > getXStep ( ) ) ;
const PDFReal yStep = qAbs ( tilingPattern - > getYStep ( ) ) ;
const QByteArray & content = tilingPattern - > getContent ( ) ;
QPainterPath boundingPath ;
boundingPath . addRect ( boundingBox ) ;
// Draw the tiling
const PDFInteger columns = qMax < PDFInteger > ( qCeil ( tilingArea . width ( ) / xStep ) , 1 ) ;
const PDFInteger rows = qMax < PDFInteger > ( qCeil ( tilingArea . height ( ) / yStep ) , 1 ) ;
QMatrix baseTransformationMatrix = m_graphicState . getCurrentTransformationMatrix ( ) ;
for ( PDFInteger column = 0 ; column < columns ; + + column )
{
for ( PDFInteger row = 0 ; row < rows ; + + row )
{
PDFPageContentProcessorGraphicStateSaveRestoreGuard guard ( this ) ;
QMatrix transformationMatrix = baseTransformationMatrix ;
transformationMatrix . translate ( tilingArea . left ( ) , tilingArea . top ( ) ) ;
transformationMatrix . translate ( column * xStep , row * yStep ) ;
m_graphicState . setCurrentTransformationMatrix ( transformationMatrix ) ;
updateGraphicState ( ) ;
performClipping ( boundingPath , boundingPath . fillRule ( ) ) ;
processContent ( content ) ;
}
}
}
2019-02-24 17:48:37 +01:00
void PDFPageContentProcessor : : processCommand ( const QByteArray & command )
{
Operator op = Operator : : Invalid ;
// Find the command in the command array
for ( const std : : pair < const char * , PDFPageContentProcessor : : Operator > & operatorDescriptor : operators )
{
if ( command = = operatorDescriptor . first )
{
op = operatorDescriptor . second ;
break ;
}
}
switch ( op )
{
case Operator : : SetLineWidth :
{
invokeOperator ( & PDFPageContentProcessor : : operatorSetLineWidth ) ;
break ;
}
case Operator : : SetLineCap :
{
invokeOperator ( & PDFPageContentProcessor : : operatorSetLineCap ) ;
break ;
}
case Operator : : SetLineJoin :
{
invokeOperator ( & PDFPageContentProcessor : : operatorSetLineJoin ) ;
break ;
}
case Operator : : SetMitterLimit :
{
invokeOperator ( & PDFPageContentProcessor : : operatorSetMitterLimit ) ;
break ;
}
case Operator : : SetLineDashPattern :
{
invokeOperator ( & PDFPageContentProcessor : : operatorSetLineDashPattern ) ;
break ;
}
case Operator : : SetRenderingIntent :
{
invokeOperator ( & PDFPageContentProcessor : : operatorSetRenderingIntent ) ;
break ;
}
case Operator : : SetFlatness :
{
invokeOperator ( & PDFPageContentProcessor : : operatorSetFlatness ) ;
break ;
}
case Operator : : SetGraphicState :
{
invokeOperator ( & PDFPageContentProcessor : : operatorSetGraphicState ) ;
break ;
}
case Operator : : SaveGraphicState :
{
operatorSaveGraphicState ( ) ;
break ;
}
case Operator : : RestoreGraphicState :
{
operatorRestoreGraphicState ( ) ;
break ;
}
case Operator : : AdjustCurrentTransformationMatrix :
{
invokeOperator ( & PDFPageContentProcessor : : operatorAdjustCurrentTransformationMatrix ) ;
break ;
}
case Operator : : MoveCurrentPoint :
{
invokeOperator ( & PDFPageContentProcessor : : operatorMoveCurrentPoint ) ;
break ;
}
case Operator : : LineTo :
{
invokeOperator ( & PDFPageContentProcessor : : operatorLineTo ) ;
break ;
}
case Operator : : Bezier123To :
{
invokeOperator ( & PDFPageContentProcessor : : operatorBezier123To ) ;
break ;
}
case Operator : : Bezier23To :
{
invokeOperator ( & PDFPageContentProcessor : : operatorBezier23To ) ;
break ;
}
case Operator : : Bezier13To :
{
invokeOperator ( & PDFPageContentProcessor : : operatorBezier13To ) ;
break ;
}
case Operator : : EndSubpath :
{
// Call directly, no parameters involved
operatorEndSubpath ( ) ;
break ;
}
case Operator : : Rectangle :
{
invokeOperator ( & PDFPageContentProcessor : : operatorRectangle ) ;
break ;
}
case Operator : : PathStroke :
{
operatorPathStroke ( ) ;
break ;
}
case Operator : : PathCloseStroke :
{
operatorPathCloseStroke ( ) ;
break ;
}
case Operator : : PathFillWinding :
case Operator : : PathFillWinding2 :
{
operatorPathFillWinding ( ) ;
break ;
}
case Operator : : PathFillEvenOdd :
{
operatorPathFillEvenOdd ( ) ;
break ;
}
case Operator : : PathFillStrokeWinding :
{
operatorPathFillStrokeWinding ( ) ;
break ;
}
case Operator : : PathFillStrokeEvenOdd :
{
operatorPathFillStrokeEvenOdd ( ) ;
break ;
}
case Operator : : PathCloseFillStrokeWinding :
{
operatorPathCloseFillStrokeWinding ( ) ;
break ;
}
case Operator : : PathCloseFillStrokeEvenOdd :
{
operatorPathCloseFillStrokeEvenOdd ( ) ;
break ;
}
case Operator : : PathClear :
{
operatorPathClear ( ) ;
break ;
}
case Operator : : ClipEvenOdd :
{
operatorClipEvenOdd ( ) ;
break ;
}
case Operator : : ClipWinding :
{
operatorClipWinding ( ) ;
break ;
}
2019-07-14 19:03:15 +02:00
case Operator : : Type3FontSetOffset :
{
// d0, set width information, see PDF 1.7 Reference, Table 5.10
invokeOperator ( & PDFPageContentProcessor : : operatorType3FontSetOffset ) ;
break ;
}
case Operator : : Type3FontSetOffsetAndBB :
{
// d1, set offset and glyph bounding box
invokeOperator ( & PDFPageContentProcessor : : operatorType3FontSetOffsetAndBB ) ;
break ;
}
2019-02-24 17:48:37 +01:00
case Operator : : ColorSetStrokingColorSpace :
{
// CS, set current color space for stroking operations
invokeOperator ( & PDFPageContentProcessor : : operatorColorSetStrokingColorSpace ) ;
break ;
}
case Operator : : ColorSetFillingColorSpace :
{
// cs, set current color space for filling operations
invokeOperator ( & PDFPageContentProcessor : : operatorColorSetFillingColorSpace ) ;
break ;
}
case Operator : : ColorSetStrokingColor :
{
// SC, set current stroking color
operatorColorSetStrokingColor ( ) ;
break ;
}
case Operator : : ColorSetStrokingColorN :
{
// SCN, same as SC, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
operatorColorSetStrokingColorN ( ) ;
break ;
}
case Operator : : ColorSetFillingColor :
{
// sc, set current filling color
operatorColorSetFillingColor ( ) ;
break ;
}
case Operator : : ColorSetFillingColorN :
{
// scn, same as sc, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
operatorColorSetFillingColorN ( ) ;
break ;
}
case Operator : : ColorSetDeviceGrayStroking :
{
// G, set DeviceGray color space for stroking color and set color
invokeOperator ( & PDFPageContentProcessor : : operatorColorSetDeviceGrayStroking ) ;
break ;
}
case Operator : : ColorSetDeviceGrayFilling :
{
// g, set DeviceGray color space for filling color and set color
invokeOperator ( & PDFPageContentProcessor : : operatorColorSetDeviceGrayFilling ) ;
break ;
}
case Operator : : ColorSetDeviceRGBStroking :
{
// RG, set DeviceRGB color space for stroking color and set color
invokeOperator ( & PDFPageContentProcessor : : operatorColorSetDeviceRGBStroking ) ;
break ;
}
case Operator : : ColorSetDeviceRGBFilling :
{
// rg, set DeviceRGB color space for filling color and set color
invokeOperator ( & PDFPageContentProcessor : : operatorColorSetDeviceRGBFilling ) ;
break ;
}
case Operator : : ColorSetDeviceCMYKStroking :
{
// K, set DeviceCMYK color space for stroking color and set color
invokeOperator ( & PDFPageContentProcessor : : operatorColorSetDeviceCMYKStroking ) ;
break ;
}
case Operator : : ColorSetDeviceCMYKFilling :
{
// k, set DeviceCMYK color space for filling color and set color
invokeOperator ( & PDFPageContentProcessor : : operatorColorSetDeviceCMYKFilling ) ;
break ;
}
2019-03-25 18:44:45 +01:00
case Operator : : TextBegin :
{
// BT, begin text object, initialize text matrices, cannot be nested
operatorTextBegin ( ) ;
break ;
}
case Operator : : TextEnd :
{
// ET, end text object, cannot be nested
operatorTextEnd ( ) ;
break ;
}
case Operator : : TextSetCharacterSpacing :
{
// Tc, set text character spacing
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetCharacterSpacing ) ;
break ;
}
case Operator : : TextSetWordSpacing :
{
// Tw, set text word spacing
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetWordSpacing ) ;
break ;
}
case Operator : : TextSetHorizontalScale :
{
// Tz, set text horizontal scaling (in percents, 100% = normal scaling)
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetHorizontalScale ) ;
break ;
}
case Operator : : TextSetLeading :
{
// TL, set text leading
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetLeading ) ;
break ;
}
case Operator : : TextSetFontAndFontSize :
{
// Tf, set text font (name from dictionary) and its size
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetFontAndFontSize ) ;
break ;
}
case Operator : : TextSetRenderMode :
{
// Tr, set text render mode
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetRenderMode ) ;
break ;
}
case Operator : : TextSetRise :
{
// Ts, set text rise
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetRise ) ;
break ;
}
case Operator : : TextMoveByOffset :
{
// Td, move by offset
invokeOperator ( & PDFPageContentProcessor : : operatorTextMoveByOffset ) ;
break ;
}
case Operator : : TextSetLeadingAndMoveByOffset :
{
// TD, sets text leading and moves by offset, x y TD is equivalent to sequence -y TL x y Td
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetLeadingAndMoveByOffset ) ;
break ;
}
case Operator : : TextSetMatrix :
{
// Tm, set text matrix
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetMatrix ) ;
break ;
}
case Operator : : TextMoveByLeading :
{
// T*, moves text by leading, equivalent to 0 leading Td
operatorTextMoveByLeading ( ) ;
break ;
}
2019-03-30 18:45:30 +01:00
case Operator : : TextShowTextString :
{
// Tj, show text string
invokeOperator ( & PDFPageContentProcessor : : operatorTextShowTextString ) ;
break ;
}
case Operator : : TextShowTextIndividualSpacing :
{
// TJ, show text, allow individual text spacing
invokeOperator ( & PDFPageContentProcessor : : operatorTextShowTextIndividualSpacing ) ;
break ;
}
case Operator : : TextNextLineShowText :
{
// ', move to the next line and show text ("string '" is equivalent to "T* string Tj")
invokeOperator ( & PDFPageContentProcessor : : operatorTextNextLineShowText ) ;
break ;
}
case Operator : : TextSetSpacingAndShowText :
{
// ", move to the next line, set spacing and show text (equivalent to sequence "w1 Tw w2 Tc string '")
invokeOperator ( & PDFPageContentProcessor : : operatorTextSetSpacingAndShowText ) ;
break ;
}
2019-05-07 18:21:22 +02:00
case Operator : : PaintXObject :
{
// Do, paint the X Object (image, form, ...)
invokeOperator ( & PDFPageContentProcessor : : operatorPaintXObject ) ;
break ;
}
2019-08-25 18:16:37 +02:00
case Operator : : ShadingPaintShape :
{
// sh, paint shape
invokeOperator ( & PDFPageContentProcessor : : operatorShadingPaintShape ) ;
break ;
}
2019-07-04 17:52:38 +02:00
case Operator : : MarkedContentPoint :
{
// MP, marked content point
invokeOperator ( & PDFPageContentProcessor : : operatorMarkedContentPoint ) ;
break ;
}
case Operator : : MarkedContentPointWithProperties :
{
// DP, marked content point with properties
operatorMarkedContentPointWithProperties ( readOperand < PDFOperandName > ( 0 ) , readObjectFromOperandStack ( 1 ) ) ;
break ;
}
case Operator : : MarkedContentBegin :
{
// BMC, begin of sequence of marked content
invokeOperator ( & PDFPageContentProcessor : : operatorMarkedContentBegin ) ;
break ;
}
case Operator : : MarkedContentBeginWithProperties :
{
// BDC, begin of sequence of marked content with properties
operatorMarkedContentBeginWithProperties ( readOperand < PDFOperandName > ( 0 ) , readObjectFromOperandStack ( 1 ) ) ;
break ;
}
case Operator : : MarkedContentEnd :
{
// EMC, end of marked content sequence
operatorMarkedContentEnd ( ) ;
break ;
}
2019-06-23 18:35:32 +02:00
case Operator : : CompatibilityBegin :
{
operatorCompatibilityBegin ( ) ;
break ;
}
case Operator : : CompatibilityEnd :
{
operatorCompatibilityEnd ( ) ;
break ;
}
2019-02-24 17:48:37 +01:00
case Operator : : Invalid :
{
m_errorList . append ( PDFRenderError ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Unknown operator '%1'. " ) . arg ( QString : : fromLatin1 ( command ) ) ) ) ;
break ;
}
default :
{
m_errorList . append ( PDFRenderError ( RenderErrorType : : NotImplemented , PDFTranslationContext : : tr ( " Not implemented operator '%1'. " ) . arg ( QString : : fromLatin1 ( command ) ) ) ) ;
break ;
}
}
}
QPointF PDFPageContentProcessor : : getCurrentPoint ( ) const
{
const int elementCount = m_currentPath . elementCount ( ) ;
if ( elementCount > 0 )
{
QPainterPath : : Element element = m_currentPath . elementAt ( elementCount - 1 ) ;
return QPointF ( element . x , element . y ) ;
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Current point of path is not set. Path is empty. " ) ) ;
}
return QPointF ( ) ;
}
void PDFPageContentProcessor : : updateGraphicState ( )
{
if ( m_graphicState . getStateFlags ( ) )
{
performUpdateGraphicsState ( m_graphicState ) ;
m_graphicState . setStateFlags ( PDFPageContentProcessorState : : StateUnchanged ) ;
}
}
void PDFPageContentProcessor : : operatorSetLineWidth ( PDFReal lineWidth )
{
lineWidth = qMax ( 0.0 , lineWidth ) ;
m_graphicState . setLineWidth ( lineWidth ) ;
updateGraphicState ( ) ;
}
Qt : : PenCapStyle PDFPageContentProcessor : : convertLineCapToPenCapStyle ( PDFInteger lineCap )
{
lineCap = qBound < PDFInteger > ( 0 , lineCap , 2 ) ;
Qt : : PenCapStyle penCapStyle = Qt : : FlatCap ;
switch ( lineCap )
{
case 0 :
{
penCapStyle = Qt : : FlatCap ;
break ;
}
case 1 :
{
penCapStyle = Qt : : RoundCap ;
break ;
}
case 2 :
{
penCapStyle = Qt : : SquareCap ;
break ;
}
default :
{
// This case can't occur, because we are correcting invalid values above.
Q_ASSERT ( false ) ;
break ;
}
}
return penCapStyle ;
}
PDFInteger PDFPageContentProcessor : : convertPenCapStyleToLineCap ( Qt : : PenCapStyle penCapStyle )
{
switch ( penCapStyle )
{
case Qt : : FlatCap :
return 0 ;
case Qt : : SquareCap :
return 2 ;
case Qt : : RoundCap :
return 1 ;
default :
break ;
}
// Invalid pen cap style occured
Q_ASSERT ( false ) ;
return 0 ;
}
void PDFPageContentProcessor : : operatorSetLineCap ( PDFInteger lineCap )
{
const Qt : : PenCapStyle penCapStyle = convertLineCapToPenCapStyle ( lineCap ) ;
m_graphicState . setLineCapStyle ( penCapStyle ) ;
updateGraphicState ( ) ;
}
Qt : : PenJoinStyle PDFPageContentProcessor : : convertLineJoinToPenJoinStyle ( PDFInteger lineJoin )
{
lineJoin = qBound < PDFInteger > ( 0 , lineJoin , 2 ) ;
Qt : : PenJoinStyle penJoinStyle = Qt : : MiterJoin ;
switch ( lineJoin )
{
case 0 :
{
penJoinStyle = Qt : : MiterJoin ;
break ;
}
case 1 :
{
penJoinStyle = Qt : : RoundJoin ;
break ;
}
case 2 :
{
penJoinStyle = Qt : : BevelJoin ;
break ;
}
default :
{
// This case can't occur, because we are correcting invalid values above.
Q_ASSERT ( false ) ;
break ;
}
}
return penJoinStyle ;
}
PDFInteger PDFPageContentProcessor : : convertPenJoinStyleToLineJoin ( Qt : : PenJoinStyle penJoinStyle )
{
switch ( penJoinStyle )
{
case Qt : : MiterJoin :
return 0 ;
case Qt : : BevelJoin :
return 2 ;
case Qt : : RoundJoin :
return 1 ;
default :
break ;
}
// Invalid pen join style occured
Q_ASSERT ( false ) ;
return 0 ;
}
void PDFPageContentProcessor : : operatorSetLineJoin ( PDFInteger lineJoin )
{
const Qt : : PenJoinStyle penJoinStyle = convertLineJoinToPenJoinStyle ( lineJoin ) ;
m_graphicState . setLineJoinStyle ( penJoinStyle ) ;
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorSetMitterLimit ( PDFReal mitterLimit )
{
mitterLimit = qMax ( 0.0 , mitterLimit ) ;
m_graphicState . setMitterLimit ( mitterLimit ) ;
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorSetLineDashPattern ( )
{
// Operand stack must be of this form [ ... numbers ... ] offset. We check it.
// Minimal number of operands is [] 0.
if ( m_operands . size ( ) < 3 )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid line dash pattern. " ) ) ;
}
// Now, we have at least 3 arguments. Check we have an array
if ( m_operands [ 0 ] . type ! = PDFLexicalAnalyzer : : TokenType : : ArrayStart | |
m_operands [ m_operands . size ( ) - 2 ] . type ! = PDFLexicalAnalyzer : : TokenType : : ArrayEnd )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid line dash pattern. " ) ) ;
}
const size_t dashArrayStartIndex = 1 ;
const size_t dashArrayEndIndex = m_operands . size ( ) - 2 ;
const size_t dashOffsetIndex = m_operands . size ( ) - 1 ;
std : : vector < PDFReal > dashArray ;
dashArray . reserve ( dashArrayEndIndex - dashArrayStartIndex ) ;
for ( size_t i = dashArrayStartIndex ; i < dashArrayEndIndex ; + + i )
{
dashArray . push_back ( readOperand < PDFReal > ( i ) ) ;
}
const PDFReal dashOffset = readOperand < PDFReal > ( dashOffsetIndex ) ;
PDFLineDashPattern pattern ( std : : move ( dashArray ) , dashOffset ) ;
m_graphicState . setLineDashPattern ( std : : move ( pattern ) ) ;
updateGraphicState ( ) ;
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : operatorSetRenderingIntent ( PDFOperandName intent )
2019-02-24 17:48:37 +01:00
{
2019-10-04 17:21:26 +02:00
setRenderingIntentByName ( intent . name ) ;
2019-02-24 17:48:37 +01:00
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorSetFlatness ( PDFReal flatness )
{
flatness = qBound ( 0.0 , flatness , 100.0 ) ;
m_graphicState . setFlatness ( flatness ) ;
updateGraphicState ( ) ;
}
2019-10-04 17:21:26 +02:00
void PDFPageContentProcessor : : setRenderingIntentByName ( QByteArray renderingIntentName )
{
RenderingIntent renderingIntent = RenderingIntent : : Unknown ;
if ( renderingIntentName = = " Perceptual " )
{
renderingIntent = RenderingIntent : : Perceptual ;
}
else if ( renderingIntentName = = " AbsoluteColorimetric " )
{
renderingIntent = RenderingIntent : : AbsoluteColorimetric ;
}
else if ( renderingIntentName = = " RelativeColorimetric " )
{
renderingIntent = RenderingIntent : : RelativeColorimetric ;
}
else if ( renderingIntentName = = " Saturation " )
{
renderingIntent = RenderingIntent : : Saturation ;
}
m_graphicState . setRenderingIntent ( renderingIntent ) ;
m_graphicState . setRenderingIntentName ( renderingIntentName ) ;
}
2019-10-05 13:08:32 +02:00
void PDFPageContentProcessor : : reportRenderErrorOnce ( RenderErrorType type , QString message )
{
if ( ! m_onceReportedErrors . count ( message ) )
{
m_onceReportedErrors . insert ( message ) ;
reportRenderError ( type , message ) ;
}
}
2019-09-27 18:41:56 +02:00
void PDFPageContentProcessor : : processApplyGraphicState ( const PDFDictionary * graphicStateDictionary )
{
PDFDocumentDataLoaderDecorator loader ( m_document ) ;
const PDFReal lineWidth = loader . readNumberFromDictionary ( graphicStateDictionary , " LW " , m_graphicState . getLineWidth ( ) ) ;
const Qt : : PenCapStyle penCapStyle = convertLineCapToPenCapStyle ( loader . readNumberFromDictionary ( graphicStateDictionary , " LC " , convertPenCapStyleToLineCap ( m_graphicState . getLineCapStyle ( ) ) ) ) ;
const Qt : : PenJoinStyle penJoinStyle = convertLineJoinToPenJoinStyle ( loader . readNumberFromDictionary ( graphicStateDictionary , " LJ " , convertPenJoinStyleToLineJoin ( m_graphicState . getLineJoinStyle ( ) ) ) ) ;
const PDFReal mitterLimit = loader . readNumberFromDictionary ( graphicStateDictionary , " MT " , m_graphicState . getMitterLimit ( ) ) ;
const PDFObject & lineDashPatternObject = m_document - > getObject ( graphicStateDictionary - > get ( " D " ) ) ;
if ( lineDashPatternObject . isArray ( ) )
{
const PDFArray * lineDashPatternDefinitionArray = lineDashPatternObject . getArray ( ) ;
if ( lineDashPatternDefinitionArray - > getCount ( ) = = 2 )
{
PDFLineDashPattern pattern ( loader . readNumberArray ( lineDashPatternDefinitionArray - > getItem ( 0 ) ) , loader . readNumber ( lineDashPatternDefinitionArray - > getItem ( 1 ) , 0.0 ) ) ;
m_graphicState . setLineDashPattern ( pattern ) ;
}
}
const PDFReal flatness = loader . readNumberFromDictionary ( graphicStateDictionary , " FL " , m_graphicState . getFlatness ( ) ) ;
const PDFReal smoothness = loader . readNumberFromDictionary ( graphicStateDictionary , " SM " , m_graphicState . getSmoothness ( ) ) ;
const bool textKnockout = loader . readBooleanFromDictionary ( graphicStateDictionary , " TK " , m_graphicState . getTextKnockout ( ) ) ;
2019-09-29 15:44:35 +02:00
const PDFReal strokingAlpha = loader . readNumberFromDictionary ( graphicStateDictionary , " CA " , m_graphicState . getAlphaStroking ( ) ) ;
const PDFReal fillingAlpha = loader . readNumberFromDictionary ( graphicStateDictionary , " ca " , m_graphicState . getAlphaFilling ( ) ) ;
QByteArray blendModeName = loader . readNameFromDictionary ( graphicStateDictionary , " BM " ) ;
2019-10-04 17:21:26 +02:00
QByteArray renderingIntentName = loader . readNameFromDictionary ( graphicStateDictionary , " RI " ) ;
const bool alphaIsShape = loader . readBooleanFromDictionary ( graphicStateDictionary , " AIS " , m_graphicState . getAlphaIsShape ( ) ) ;
2019-09-27 18:41:56 +02:00
2019-09-29 15:44:35 +02:00
if ( ! blendModeName . isEmpty ( ) )
2019-09-27 18:41:56 +02:00
{
2019-09-29 15:44:35 +02:00
BlendMode blendMode = PDFBlendModeInfo : : getBlendMode ( blendModeName ) ;
if ( blendMode = = BlendMode : : Invalid )
{
blendMode = BlendMode : : Normal ;
m_errorList . append ( PDFRenderError ( RenderErrorType : : NotImplemented , PDFTranslationContext : : tr ( " Blend mode '%1' is invalid. " ) . arg ( QString : : fromLatin1 ( blendModeName ) ) ) ) ;
}
m_graphicState . setBlendMode ( blendMode ) ;
2019-09-27 18:41:56 +02:00
}
2019-10-04 17:21:26 +02:00
if ( ! renderingIntentName . isEmpty ( ) )
{
setRenderingIntentByName ( renderingIntentName ) ;
}
PDFOverprintMode overprintMode = m_graphicState . getOverprintMode ( ) ;
overprintMode . overprintMode = loader . readIntegerFromDictionary ( graphicStateDictionary , " OPM " , overprintMode . overprintMode ) ;
if ( graphicStateDictionary - > hasKey ( " OP " ) )
{
// Overprint for filling is ruled by overprint for stroking, if "op" is not present, according to the specification
overprintMode . overprintStroking = loader . readBooleanFromDictionary ( graphicStateDictionary , " OP " , overprintMode . overprintStroking ) ;
overprintMode . overprintFilling = loader . readBooleanFromDictionary ( graphicStateDictionary , " op " , overprintMode . overprintStroking ) ;
}
2019-09-27 18:41:56 +02:00
m_graphicState . setLineWidth ( lineWidth ) ;
m_graphicState . setLineCapStyle ( penCapStyle ) ;
m_graphicState . setLineJoinStyle ( penJoinStyle ) ;
m_graphicState . setMitterLimit ( mitterLimit ) ;
m_graphicState . setFlatness ( flatness ) ;
m_graphicState . setSmoothness ( smoothness ) ;
m_graphicState . setTextKnockout ( textKnockout ) ;
2019-09-29 15:44:35 +02:00
m_graphicState . setAlphaStroking ( strokingAlpha ) ;
m_graphicState . setAlphaFilling ( fillingAlpha ) ;
2019-10-04 17:21:26 +02:00
m_graphicState . setOverprintMode ( overprintMode ) ;
m_graphicState . setAlphaIsShape ( alphaIsShape ) ;
2019-09-27 18:41:56 +02:00
updateGraphicState ( ) ;
2019-10-04 17:21:26 +02:00
if ( graphicStateDictionary - > hasKey ( " Font " ) )
{
const PDFObject & fontObject = m_document - > getObject ( graphicStateDictionary - > get ( " Font " ) ) ;
if ( fontObject . isArray ( ) )
{
const PDFArray * fontObjectArray = fontObject . getArray ( ) ;
if ( fontObjectArray - > getCount ( ) = = 2 )
{
PDFOperandName operandName ;
operandName . name = loader . readName ( fontObjectArray - > getItem ( 0 ) ) ;
operatorTextSetFontAndFontSize ( operandName , loader . readNumber ( fontObjectArray - > getItem ( 1 ) , 1.0 ) ) ;
}
}
}
if ( graphicStateDictionary - > hasKey ( " SMask " ) )
{
const PDFObject & softMaskObject = m_document - > getObject ( graphicStateDictionary - > get ( " SMask " ) ) ;
bool isNone = ( softMaskObject . isName ( ) & & softMaskObject . getString ( ) = = " None " ) ;
if ( ! isNone )
{
2019-10-05 13:08:32 +02:00
reportRenderErrorOnce ( RenderErrorType : : NotSupported , PDFTranslationContext : : tr ( " Soft masks not supported. " ) ) ;
2019-10-04 17:21:26 +02:00
}
}
2019-09-27 18:41:56 +02:00
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : operatorSetGraphicState ( PDFOperandName dictionaryName )
2019-02-24 17:48:37 +01:00
{
2019-06-16 16:32:23 +02:00
if ( m_extendedGraphicStateDictionary )
2019-02-24 17:48:37 +01:00
{
2019-06-16 16:32:23 +02:00
if ( m_extendedGraphicStateDictionary - > hasKey ( dictionaryName . name ) )
2019-02-24 17:48:37 +01:00
{
2019-06-16 16:32:23 +02:00
const PDFObject & graphicStateObject = m_document - > getObject ( m_extendedGraphicStateDictionary - > get ( dictionaryName . name ) ) ;
if ( graphicStateObject . isDictionary ( ) )
2019-02-24 17:48:37 +01:00
{
2019-06-16 16:32:23 +02:00
const PDFDictionary * graphicStateDictionary = graphicStateObject . getDictionary ( ) ;
2019-09-27 18:41:56 +02:00
processApplyGraphicState ( graphicStateDictionary ) ;
2019-02-24 17:48:37 +01:00
}
else
{
2019-06-16 16:32:23 +02:00
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Graphic state '%1' found, but invalid in resource dictionary. " ) . arg ( QString : : fromLatin1 ( dictionaryName . name ) ) ) ;
2019-02-24 17:48:37 +01:00
}
}
else
{
2019-06-16 16:32:23 +02:00
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Graphic state '%1' not found in resource dictionary. " ) . arg ( QString : : fromLatin1 ( dictionaryName . name ) ) ) ;
2019-02-24 17:48:37 +01:00
}
}
else
{
2019-06-16 16:32:23 +02:00
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid graphic state resource dictionary. " ) ) ;
2019-02-24 17:48:37 +01:00
}
}
void PDFPageContentProcessor : : operatorSaveGraphicState ( )
{
performSaveGraphicState ( ProcessOrder : : BeforeOperation ) ;
m_stack . push ( m_graphicState ) ;
m_stack . top ( ) . setStateFlags ( PDFPageContentProcessorState : : StateUnchanged ) ;
performSaveGraphicState ( ProcessOrder : : AfterOperation ) ;
}
void PDFPageContentProcessor : : operatorRestoreGraphicState ( )
{
if ( m_stack . empty ( ) )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Trying to restore graphic state more times than it was saved. " ) ) ;
}
performRestoreGraphicState ( ProcessOrder : : BeforeOperation ) ;
m_graphicState = m_stack . top ( ) ;
m_stack . pop ( ) ;
updateGraphicState ( ) ;
performRestoreGraphicState ( ProcessOrder : : AfterOperation ) ;
}
void PDFPageContentProcessor : : operatorAdjustCurrentTransformationMatrix ( PDFReal a , PDFReal b , PDFReal c , PDFReal d , PDFReal e , PDFReal f )
{
// We will comment following equation:
// Adobe PDF Reference 1.7 says, that we have this transformation using coefficient a, b, c, d, e and f:
// [ a, b, 0 ]
// [x', y', 1] = [ x, y, 1] * [ c, d, 0 ]
// [ e, f, 1 ]
// If we transpose this equation (we want this, because Qt uses transposed matrices (QMatrix).
// So, we will get following result:
//
// [ x' ] [ a, c, e] [ x ]
// [ y' ] = [ b, d, f] * [ y ]
// [ 1 ] [ 0, 0, 1] [ 1 ]
//
// So, it is obvious, than we will have following coefficients:
// m_11 = a, m_21 = c, dx = e
// m_12 = b, m_22 = d, dy = f
//
// We must also check, that matrix is invertible. If it is not, then we will throw exception
// to avoid errors later (for some operations, we assume matrix is invertible).
QMatrix matrix ( a , b , c , d , e , f ) ;
2019-04-27 14:39:20 +02:00
QMatrix transformMatrix = matrix * m_graphicState . getCurrentTransformationMatrix ( ) ;
2019-02-24 17:48:37 +01:00
if ( ! transformMatrix . isInvertible ( ) )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Transformation matrix is not invertible. " ) ) ;
}
m_graphicState . setCurrentTransformationMatrix ( transformMatrix ) ;
updateGraphicState ( ) ;
}
template < >
PDFReal PDFPageContentProcessor : : readOperand < PDFReal > ( size_t index ) const
{
if ( index < m_operands . size ( ) )
{
const PDFLexicalAnalyzer : : Token & token = m_operands [ index ] ;
switch ( token . type )
{
case PDFLexicalAnalyzer : : TokenType : : Real :
case PDFLexicalAnalyzer : : TokenType : : Integer :
return token . data . value < PDFReal > ( ) ;
default :
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Can't read operand (real number) on index % 1. Operand is of type ' % 2 ' . " ).arg(index + 1).arg(PDFLexicalAnalyzer::getStringFromOperandType(token.type))) ;
}
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Can't read operand (real number) on index % 1. Only % 2 operands provided . " ).arg(index + 1).arg(m_operands.size())) ;
}
return 0.0 ;
}
template < >
PDFInteger PDFPageContentProcessor : : readOperand < PDFInteger > ( size_t index ) const
{
if ( index < m_operands . size ( ) )
{
const PDFLexicalAnalyzer : : Token & token = m_operands [ index ] ;
switch ( token . type )
{
case PDFLexicalAnalyzer : : TokenType : : Integer :
return token . data . value < PDFInteger > ( ) ;
default :
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Can't read operand (integer) on index % 1. Operand is of type ' % 2 ' . " ).arg(index + 1).arg(PDFLexicalAnalyzer::getStringFromOperandType(token.type))) ;
}
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Can't read operand (integer) on index % 1. Only % 2 operands provided . " ).arg(index + 1).arg(m_operands.size())) ;
}
return 0 ;
}
template < >
2019-03-30 18:45:30 +01:00
PDFPageContentProcessor : : PDFOperandName PDFPageContentProcessor : : readOperand < PDFPageContentProcessor : : PDFOperandName > ( size_t index ) const
2019-02-24 17:48:37 +01:00
{
if ( index < m_operands . size ( ) )
{
const PDFLexicalAnalyzer : : Token & token = m_operands [ index ] ;
switch ( token . type )
{
case PDFLexicalAnalyzer : : TokenType : : Name :
2019-03-30 18:45:30 +01:00
return PDFOperandName { token . data . toByteArray ( ) } ;
2019-02-24 17:48:37 +01:00
default :
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Can't read operand (name) on index % 1. Operand is of type ' % 2 ' . " ).arg(index + 1).arg(PDFLexicalAnalyzer::getStringFromOperandType(token.type))) ;
}
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Can't read operand (name) on index % 1. Only % 2 operands provided . " ).arg(index + 1).arg(m_operands.size())) ;
}
2019-03-30 18:45:30 +01:00
return PDFOperandName ( ) ;
}
template < >
PDFPageContentProcessor : : PDFOperandString PDFPageContentProcessor : : readOperand < PDFPageContentProcessor : : PDFOperandString > ( size_t index ) const
{
if ( index < m_operands . size ( ) )
{
const PDFLexicalAnalyzer : : Token & token = m_operands [ index ] ;
switch ( token . type )
{
case PDFLexicalAnalyzer : : TokenType : : String :
return PDFOperandString { token . data . toByteArray ( ) } ;
default :
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Can't read operand (string) on index % 1. Operand is of type ' % 2 ' . " ).arg(index + 1).arg(PDFLexicalAnalyzer::getStringFromOperandType(token.type))) ;
}
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Can't read operand (string) on index % 1. Only % 2 operands provided . " ).arg(index + 1).arg(m_operands.size())) ;
}
return PDFOperandString ( ) ;
2019-02-24 17:48:37 +01:00
}
void PDFPageContentProcessor : : operatorMoveCurrentPoint ( PDFReal x , PDFReal y )
{
m_currentPath . moveTo ( x , y ) ; ;
}
void PDFPageContentProcessor : : operatorLineTo ( PDFReal x , PDFReal y )
{
m_currentPath . lineTo ( x , y ) ;
}
void PDFPageContentProcessor : : operatorBezier123To ( PDFReal x1 , PDFReal y1 , PDFReal x2 , PDFReal y2 , PDFReal x3 , PDFReal y3 )
{
m_currentPath . cubicTo ( x1 , y1 , x2 , y2 , x3 , y3 ) ;
}
void PDFPageContentProcessor : : operatorBezier23To ( PDFReal x2 , PDFReal y2 , PDFReal x3 , PDFReal y3 )
{
QPointF currentPoint = getCurrentPoint ( ) ;
m_currentPath . cubicTo ( currentPoint . x ( ) , currentPoint . y ( ) , x2 , y2 , x3 , y3 ) ;
}
void PDFPageContentProcessor : : operatorBezier13To ( PDFReal x1 , PDFReal y1 , PDFReal x3 , PDFReal y3 )
{
m_currentPath . cubicTo ( x1 , y1 , x3 , y3 , x3 , y3 ) ;
}
void PDFPageContentProcessor : : operatorEndSubpath ( )
{
m_currentPath . closeSubpath ( ) ;
}
void PDFPageContentProcessor : : operatorRectangle ( PDFReal x , PDFReal y , PDFReal width , PDFReal height )
{
2019-03-17 16:12:36 +01:00
const PDFReal xMin = qMin ( x , x + width ) ;
const PDFReal xMax = qMax ( x , x + width ) ;
const PDFReal yMin = qMin ( y , y + height ) ;
const PDFReal yMax = qMax ( y , y + height ) ;
const PDFReal correctedWidth = xMax - xMin ;
const PDFReal correctedHeight = yMax - yMin ;
m_currentPath . addRect ( QRectF ( xMin , yMin , correctedWidth , correctedHeight ) ) ;
2019-02-24 17:48:37 +01:00
}
void PDFPageContentProcessor : : operatorPathStroke ( )
{
// Do not close the path
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . setFillRule ( Qt : : WindingFill ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( m_currentPath , true , false , false , Qt : : WindingFill ) ;
2019-02-24 17:48:37 +01:00
m_currentPath = QPainterPath ( ) ;
}
}
void PDFPageContentProcessor : : operatorPathCloseStroke ( )
{
// Close the path
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . closeSubpath ( ) ;
m_currentPath . setFillRule ( Qt : : WindingFill ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( m_currentPath , true , false , false , Qt : : WindingFill ) ;
2019-02-24 17:48:37 +01:00
m_currentPath = QPainterPath ( ) ;
}
}
void PDFPageContentProcessor : : operatorPathFillWinding ( )
{
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . setFillRule ( Qt : : WindingFill ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( m_currentPath , false , true , false , Qt : : WindingFill ) ;
2019-02-24 17:48:37 +01:00
m_currentPath = QPainterPath ( ) ;
}
}
void PDFPageContentProcessor : : operatorPathFillEvenOdd ( )
{
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . setFillRule ( Qt : : OddEvenFill ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( m_currentPath , false , true , false , Qt : : OddEvenFill ) ;
2019-02-24 17:48:37 +01:00
m_currentPath = QPainterPath ( ) ;
}
}
void PDFPageContentProcessor : : operatorPathFillStrokeWinding ( )
{
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . setFillRule ( Qt : : WindingFill ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( m_currentPath , true , true , false , Qt : : WindingFill ) ;
2019-02-24 17:48:37 +01:00
m_currentPath = QPainterPath ( ) ;
}
}
void PDFPageContentProcessor : : operatorPathFillStrokeEvenOdd ( )
{
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . setFillRule ( Qt : : OddEvenFill ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( m_currentPath , true , true , false , Qt : : OddEvenFill ) ;
2019-02-24 17:48:37 +01:00
m_currentPath = QPainterPath ( ) ;
}
}
void PDFPageContentProcessor : : operatorPathCloseFillStrokeWinding ( )
{
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . closeSubpath ( ) ;
m_currentPath . setFillRule ( Qt : : WindingFill ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( m_currentPath , true , true , false , Qt : : WindingFill ) ;
2019-02-24 17:48:37 +01:00
m_currentPath = QPainterPath ( ) ;
}
}
void PDFPageContentProcessor : : operatorPathCloseFillStrokeEvenOdd ( )
{
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . closeSubpath ( ) ;
m_currentPath . setFillRule ( Qt : : OddEvenFill ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( m_currentPath , true , true , false , Qt : : OddEvenFill ) ;
2019-02-24 17:48:37 +01:00
m_currentPath = QPainterPath ( ) ;
}
}
void PDFPageContentProcessor : : operatorPathClear ( )
{
m_currentPath = QPainterPath ( ) ;
}
void PDFPageContentProcessor : : operatorClipWinding ( )
{
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . setFillRule ( Qt : : WindingFill ) ;
performClipping ( m_currentPath , Qt : : WindingFill ) ;
}
}
void PDFPageContentProcessor : : operatorClipEvenOdd ( )
{
if ( ! m_currentPath . isEmpty ( ) )
{
m_currentPath . setFillRule ( Qt : : OddEvenFill ) ;
performClipping ( m_currentPath , Qt : : OddEvenFill ) ;
}
}
2019-07-14 19:03:15 +02:00
void PDFPageContentProcessor : : operatorType3FontSetOffset ( PDFReal wx , PDFReal wy )
{
performSetCharWidth ( wx , wy ) ;
}
void PDFPageContentProcessor : : operatorType3FontSetOffsetAndBB ( PDFReal wx , PDFReal wy , PDFReal llx , PDFReal lly , PDFReal urx , PDFReal ury )
{
performSetCacheDevice ( wx , wy , llx , lly , urx , ury ) ;
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : operatorColorSetStrokingColorSpace ( PDFPageContentProcessor : : PDFOperandName name )
2019-02-24 17:48:37 +01:00
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace : : createColorSpace ( m_colorSpaceDictionary , m_document , PDFObject : : createName ( std : : make_shared < PDFString > ( QByteArray ( name . name ) ) ) ) ;
if ( colorSpace )
{
// We must also set default color (it can depend on the color space)
m_graphicState . setStrokeColorSpace ( colorSpace ) ;
2019-12-25 17:56:17 +01:00
m_graphicState . setStrokeColor ( colorSpace - > getDefaultColor ( m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ) ;
2019-02-24 17:48:37 +01:00
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkStrokingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid color space. " ) ) ;
}
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : operatorColorSetFillingColorSpace ( PDFOperandName name )
2019-02-24 17:48:37 +01:00
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace : : createColorSpace ( m_colorSpaceDictionary , m_document , PDFObject : : createName ( std : : make_shared < PDFString > ( QByteArray ( name . name ) ) ) ) ;
if ( colorSpace )
{
// We must also set default color (it can depend on the color space)
m_graphicState . setFillColorSpace ( colorSpace ) ;
2019-12-25 17:56:17 +01:00
m_graphicState . setFillColor ( colorSpace - > getDefaultColor ( m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ) ;
2019-02-24 17:48:37 +01:00
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkFillingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid color space. " ) ) ;
}
}
void PDFPageContentProcessor : : operatorColorSetStrokingColor ( )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
const PDFAbstractColorSpace * colorSpace = m_graphicState . getStrokeColorSpace ( ) ;
const size_t colorSpaceComponentCount = colorSpace - > getColorComponentCount ( ) ;
const size_t operandCount = m_operands . size ( ) ;
if ( operandCount = = colorSpaceComponentCount )
{
PDFColor color ;
for ( size_t i = 0 ; i < operandCount ; + + i )
{
color . push_back ( readOperand < PDFReal > ( i ) ) ;
}
2019-12-25 17:56:17 +01:00
m_graphicState . setStrokeColor ( colorSpace - > getColor ( color , m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ) ;
2019-02-24 17:48:37 +01:00
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkStrokingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid color component count. Provided %1, required %2. " ) . arg ( operandCount ) . arg ( colorSpaceComponentCount ) ) ;
}
}
void PDFPageContentProcessor : : operatorColorSetStrokingColorN ( )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-03-17 16:12:36 +01:00
// In our implementation, operator 'SC' can also set color using all color spaces
// PDF reference 1.7 allows here Pattern, Separation, DeviceN and ICCBased color spaces here,
2019-09-13 16:28:20 +02:00
// but default operator can use them (with exception of Pattern color space). For pattern color space,
// we treat this differently.
const PDFAbstractColorSpace * colorSpace = m_graphicState . getStrokeColorSpace ( ) ;
2019-09-26 19:14:04 +02:00
if ( const PDFPatternColorSpace * patternColorSpace = colorSpace - > asPatternColorSpace ( ) )
2019-09-13 16:28:20 +02:00
{
2019-09-26 19:14:04 +02:00
const size_t operandCount = m_operands . size ( ) ;
if ( operandCount > 0 )
2019-09-13 16:28:20 +02:00
{
2019-09-26 19:14:04 +02:00
PDFColorSpacePointer uncoloredColorSpace = patternColorSpace - > getUncoloredPatternColorSpace ( ) ;
PDFColor uncoloredPatternColor ;
for ( size_t i = 0 ; i < operandCount - 1 ; + + i )
{
uncoloredPatternColor . push_back ( readOperand < PDFReal > ( i ) ) ;
}
PDFOperandName name = readOperand < PDFOperandName > ( operandCount - 1 ) ;
2019-09-13 16:28:20 +02:00
if ( m_patternDictionary & & m_patternDictionary - > hasKey ( name . name ) )
{
// Create the pattern
2019-12-25 17:56:17 +01:00
PDFPatternPtr pattern = PDFPattern : : createPattern ( m_colorSpaceDictionary , m_document , m_patternDictionary - > get ( name . name ) , m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ;
2019-09-26 19:14:04 +02:00
m_graphicState . setStrokeColorSpace ( PDFColorSpacePointer ( new PDFPatternColorSpace ( qMove ( pattern ) , qMove ( uncoloredColorSpace ) , qMove ( uncoloredPatternColor ) ) ) ) ;
2019-09-13 16:28:20 +02:00
updateGraphicState ( ) ;
return ;
}
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid pattern for Pattern color space. " ) ) ;
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid pattern for Pattern color space. " ) ) ;
}
}
else
{
operatorColorSetStrokingColor ( ) ;
}
2019-02-24 17:48:37 +01:00
}
void PDFPageContentProcessor : : operatorColorSetFillingColor ( )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
const PDFAbstractColorSpace * colorSpace = m_graphicState . getFillColorSpace ( ) ;
const size_t colorSpaceComponentCount = colorSpace - > getColorComponentCount ( ) ;
const size_t operandCount = m_operands . size ( ) ;
if ( operandCount = = colorSpaceComponentCount )
{
PDFColor color ;
for ( size_t i = 0 ; i < operandCount ; + + i )
{
color . push_back ( readOperand < PDFReal > ( i ) ) ;
}
2019-12-25 17:56:17 +01:00
m_graphicState . setFillColor ( colorSpace - > getColor ( color , m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ) ;
2019-02-24 17:48:37 +01:00
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkFillingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid color component count. Provided %1, required %2. " ) . arg ( operandCount ) . arg ( colorSpaceComponentCount ) ) ;
}
}
void PDFPageContentProcessor : : operatorColorSetFillingColorN ( )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-03-17 16:12:36 +01:00
// In our implementation, operator 'sc' can also set color using all color spaces
// PDF reference 1.7 allows here Pattern, Separation, DeviceN and ICCBased color spaces here,
2019-09-13 16:28:20 +02:00
// but default operator can use them (with exception of Pattern color space). For pattern color space,
// we treat this differently.
const PDFAbstractColorSpace * colorSpace = m_graphicState . getFillColorSpace ( ) ;
2019-09-26 19:14:04 +02:00
if ( const PDFPatternColorSpace * patternColorSpace = colorSpace - > asPatternColorSpace ( ) )
2019-09-13 16:28:20 +02:00
{
2019-09-26 19:14:04 +02:00
const size_t operandCount = m_operands . size ( ) ;
if ( operandCount > 0 )
2019-09-13 16:28:20 +02:00
{
2019-09-26 19:14:04 +02:00
PDFColorSpacePointer uncoloredColorSpace = patternColorSpace - > getUncoloredPatternColorSpace ( ) ;
PDFColor uncoloredPatternColor ;
for ( size_t i = 0 ; i < operandCount - 1 ; + + i )
{
uncoloredPatternColor . push_back ( readOperand < PDFReal > ( i ) ) ;
}
PDFOperandName name = readOperand < PDFOperandName > ( operandCount - 1 ) ;
2019-09-13 16:28:20 +02:00
if ( m_patternDictionary & & m_patternDictionary - > hasKey ( name . name ) )
{
// Create the pattern
2019-12-25 17:56:17 +01:00
PDFPatternPtr pattern = PDFPattern : : createPattern ( m_colorSpaceDictionary , m_document , m_patternDictionary - > get ( name . name ) , m_CMS , m_graphicState . getRenderingIntent ( ) , this ) ;
2019-09-26 19:14:04 +02:00
m_graphicState . setFillColorSpace ( QSharedPointer < PDFAbstractColorSpace > ( new PDFPatternColorSpace ( qMove ( pattern ) , qMove ( uncoloredColorSpace ) , qMove ( uncoloredPatternColor ) ) ) ) ;
2019-09-13 16:28:20 +02:00
updateGraphicState ( ) ;
return ;
}
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid pattern for Pattern color space. " ) ) ;
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid pattern for Pattern color space. " ) ) ;
}
}
else
{
operatorColorSetFillingColor ( ) ;
}
2019-02-24 17:48:37 +01:00
}
void PDFPageContentProcessor : : operatorColorSetDeviceGrayStroking ( PDFReal gray )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
m_graphicState . setStrokeColorSpace ( m_deviceGrayColorSpace ) ;
m_graphicState . setStrokeColor ( getColorFromColorSpace ( m_graphicState . getStrokeColorSpace ( ) , gray ) ) ;
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkStrokingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
void PDFPageContentProcessor : : operatorColorSetDeviceGrayFilling ( PDFReal gray )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
m_graphicState . setFillColorSpace ( m_deviceGrayColorSpace ) ;
m_graphicState . setFillColor ( getColorFromColorSpace ( m_graphicState . getFillColorSpace ( ) , gray ) ) ;
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkFillingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
void PDFPageContentProcessor : : operatorColorSetDeviceRGBStroking ( PDFReal r , PDFReal g , PDFReal b )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
m_graphicState . setStrokeColorSpace ( m_deviceRGBColorSpace ) ;
m_graphicState . setStrokeColor ( getColorFromColorSpace ( m_graphicState . getStrokeColorSpace ( ) , r , g , b ) ) ;
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkStrokingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
void PDFPageContentProcessor : : operatorColorSetDeviceRGBFilling ( PDFReal r , PDFReal g , PDFReal b )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
m_graphicState . setFillColorSpace ( m_deviceRGBColorSpace ) ;
m_graphicState . setFillColor ( getColorFromColorSpace ( m_graphicState . getFillColorSpace ( ) , r , g , b ) ) ;
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkFillingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
void PDFPageContentProcessor : : operatorColorSetDeviceCMYKStroking ( PDFReal c , PDFReal m , PDFReal y , PDFReal k )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
m_graphicState . setStrokeColorSpace ( m_deviceCMYKColorSpace ) ;
m_graphicState . setStrokeColor ( getColorFromColorSpace ( m_graphicState . getStrokeColorSpace ( ) , c , m , y , k ) ) ;
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkStrokingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
void PDFPageContentProcessor : : operatorColorSetDeviceCMYKFilling ( PDFReal c , PDFReal m , PDFReal y , PDFReal k )
{
2019-09-27 18:41:56 +02:00
if ( m_drawingUncoloredTilingPatternState )
{
reportWarningAboutColorOperatorsInUTP ( ) ;
return ;
}
2019-02-24 17:48:37 +01:00
m_graphicState . setFillColorSpace ( m_deviceCMYKColorSpace ) ;
m_graphicState . setFillColor ( getColorFromColorSpace ( m_graphicState . getFillColorSpace ( ) , c , m , y , k ) ) ;
updateGraphicState ( ) ;
2019-04-29 14:14:06 +02:00
checkFillingColor ( ) ;
2019-02-24 17:48:37 +01:00
}
2019-03-25 18:44:45 +01:00
void PDFPageContentProcessor : : operatorTextBegin ( )
{
m_graphicState . setTextMatrix ( QMatrix ( ) ) ;
m_graphicState . setTextLineMatrix ( QMatrix ( ) ) ;
updateGraphicState ( ) ;
+ + m_textBeginEndState ;
if ( m_textBeginEndState > 1 )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Text object already started. " ) ) ;
}
}
void PDFPageContentProcessor : : operatorTextEnd ( )
{
if ( - - m_textBeginEndState < 0 )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Text object ended more than once. " ) ) ;
}
2019-04-29 18:13:16 +02:00
if ( ! m_textClippingPath . isEmpty ( ) )
{
QPainterPath clippingPath = m_graphicState . getCurrentTransformationMatrix ( ) . inverted ( ) . map ( m_textClippingPath ) ;
performClipping ( clippingPath , clippingPath . fillRule ( ) ) ;
m_textClippingPath = QPainterPath ( ) ;
}
2019-03-25 18:44:45 +01:00
}
void PDFPageContentProcessor : : operatorTextSetCharacterSpacing ( PDFReal charSpacing )
{
m_graphicState . setTextCharacterSpacing ( charSpacing ) ;
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorTextSetWordSpacing ( PDFReal wordSpacing )
{
m_graphicState . setTextWordSpacing ( wordSpacing ) ;
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorTextSetHorizontalScale ( PDFReal horizontalScaling )
{
// We disable horizontal scaling to less than 1%
horizontalScaling = qMax ( horizontalScaling , 1.0 ) ;
2019-08-14 19:07:53 +02:00
m_graphicState . setTextHorizontalScaling ( horizontalScaling ) ;
2019-03-25 18:44:45 +01:00
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorTextSetLeading ( PDFReal leading )
{
m_graphicState . setTextLeading ( leading ) ;
updateGraphicState ( ) ;
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : operatorTextSetFontAndFontSize ( PDFOperandName fontName , PDFReal fontSize )
2019-03-25 18:44:45 +01:00
{
2019-03-30 18:45:30 +01:00
if ( m_fontDictionary )
{
if ( m_fontDictionary - > hasKey ( fontName . name ) )
{
2019-04-06 17:32:36 +02:00
try
{
2019-04-12 19:17:19 +02:00
PDFFontPointer font = m_fontCache - > getFont ( m_fontDictionary - > get ( fontName . name ) ) ;
2019-03-25 18:44:45 +01:00
2019-04-06 17:32:36 +02:00
m_graphicState . setTextFont ( qMove ( font ) ) ;
m_graphicState . setTextFontSize ( fontSize ) ;
updateGraphicState ( ) ;
}
2019-09-27 18:41:56 +02:00
catch ( PDFException )
2019-04-06 17:32:36 +02:00
{
m_graphicState . setTextFont ( nullptr ) ;
m_graphicState . setTextFontSize ( fontSize ) ;
updateGraphicState ( ) ;
throw ;
}
2019-03-30 18:45:30 +01:00
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Font '%1' not found in font dictionary. " ) . arg ( QString : : fromLatin1 ( fontName . name ) ) ) ;
}
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid font dictionary. " ) ) ;
}
2019-03-25 18:44:45 +01:00
}
void PDFPageContentProcessor : : operatorTextSetRenderMode ( PDFInteger mode )
{
mode = qBound < PDFInteger > ( 0 , mode , 7 ) ;
m_graphicState . setTextRenderingMode ( static_cast < TextRenderingMode > ( mode ) ) ;
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorTextSetRise ( PDFReal rise )
{
m_graphicState . setTextRise ( rise ) ;
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorTextMoveByOffset ( PDFReal t_x , PDFReal t_y )
{
const QMatrix & textLineMatrix = m_graphicState . getTextLineMatrix ( ) ;
2019-04-27 14:39:20 +02:00
QMatrix transformedMatrix = QMatrix ( 1 , 0 , 0 , 1 , t_x , t_y ) * textLineMatrix ;
2019-03-25 18:44:45 +01:00
2019-03-31 18:08:36 +02:00
m_graphicState . setTextMatrix ( transformedMatrix ) ;
m_graphicState . setTextLineMatrix ( transformedMatrix ) ;
2019-03-25 18:44:45 +01:00
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorTextSetLeadingAndMoveByOffset ( PDFReal t_x , PDFReal t_y )
{
// Update of graphic state is
m_graphicState . setTextLeading ( - t_y ) ;
operatorTextMoveByOffset ( t_x , t_y ) ;
}
void PDFPageContentProcessor : : operatorTextSetMatrix ( PDFReal a , PDFReal b , PDFReal c , PDFReal d , PDFReal e , PDFReal f )
{
// We will comment following equation:
// Adobe PDF Reference 1.7 says, that we have this transformation using coefficient a, b, c, d, e and f:
// [ a, b, 0 ]
// [x', y', 1] = [ x, y, 1] * [ c, d, 0 ]
// [ e, f, 1 ]
// If we transpose this equation (we want this, because Qt uses transposed matrices (QMatrix).
// So, we will get following result:
//
// [ x' ] [ a, c, e] [ x ]
// [ y' ] = [ b, d, f] * [ y ]
// [ 1 ] [ 0, 0, 1] [ 1 ]
//
// So, it is obvious, than we will have following coefficients:
// m_11 = a, m_21 = c, dx = e
// m_12 = b, m_22 = d, dy = f
//
// We must also check, that matrix is invertible. If it is not, then we will throw exception
// to avoid errors later (for some operations, we assume matrix is invertible).
QMatrix matrix ( a , b , c , d , e , f ) ;
m_graphicState . setTextMatrix ( matrix ) ;
m_graphicState . setTextLineMatrix ( matrix ) ;
updateGraphicState ( ) ;
}
void PDFPageContentProcessor : : operatorTextMoveByLeading ( )
{
2019-04-16 18:23:43 +02:00
operatorTextMoveByOffset ( 0.0 , - m_graphicState . getTextLeading ( ) ) ;
2019-03-25 18:44:45 +01:00
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : operatorTextShowTextString ( PDFOperandString text )
{
if ( m_graphicState . getTextFont ( ) )
{
2019-04-07 19:39:29 +02:00
// Get the realized font
const PDFRealizedFontPointer & realizedFont = getRealizedFont ( ) ;
if ( ! realizedFont )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid font, text can't be printed. " ) ) ;
}
TextSequence textSequence ;
// We use simple heuristic to ensure reallocation doesn't occur too often
textSequence . items . reserve ( m_operands . size ( ) ) ;
2019-04-30 14:39:48 +02:00
realizedFont - > fillTextSequence ( text . string , textSequence , this ) ;
2019-04-07 19:39:29 +02:00
drawText ( textSequence ) ;
2019-03-30 18:45:30 +01:00
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid font, text can't be printed. " ) ) ;
}
}
void PDFPageContentProcessor : : operatorTextShowTextIndividualSpacing ( )
{
// Operand stack must be of this form [ ... text, number, text, number, number, text ... ]. We check it.
if ( m_operands . size ( ) < 2 )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid parameters of text operator with individual character spacing. " ) ) ;
}
// Now, we have at least 2 arguments. Check we have an array
if ( m_operands [ 0 ] . type ! = PDFLexicalAnalyzer : : TokenType : : ArrayStart | |
m_operands [ m_operands . size ( ) - 1 ] . type ! = PDFLexicalAnalyzer : : TokenType : : ArrayEnd )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid line dash pattern. " ) ) ;
}
if ( m_graphicState . getTextFont ( ) )
{
TextSequence textSequence ;
// We use simple heuristic to ensure reallocation doesn't occur too often
textSequence . items . reserve ( m_operands . size ( ) * 4 ) ;
2019-04-07 19:39:29 +02:00
// Get the realized font
const PDFRealizedFontPointer & realizedFont = getRealizedFont ( ) ;
if ( ! realizedFont )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid font, text can't be printed. " ) ) ;
}
2019-03-30 18:45:30 +01:00
for ( size_t i = 1 , lastIndex = m_operands . size ( ) - 1 ; i < lastIndex ; + + i )
{
switch ( m_operands [ i ] . type )
{
case PDFLexicalAnalyzer : : TokenType : : Integer :
{
textSequence . items . push_back ( TextSequenceItem ( m_operands [ i ] . data . value < PDFInteger > ( ) ) ) ;
break ;
}
2019-03-31 14:17:01 +02:00
case PDFLexicalAnalyzer : : TokenType : : Real :
{
textSequence . items . push_back ( TextSequenceItem ( m_operands [ i ] . data . value < PDFReal > ( ) ) ) ;
break ;
}
2019-03-30 18:45:30 +01:00
case PDFLexicalAnalyzer : : TokenType : : String :
{
2019-04-30 14:39:48 +02:00
realizedFont - > fillTextSequence ( m_operands [ i ] . data . toByteArray ( ) , textSequence , this ) ;
2019-03-30 18:45:30 +01:00
break ;
}
default :
{
// Error - we have operand of different type
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid operand of text show operator. " ) ) ;
}
}
}
drawText ( textSequence ) ;
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid font, text can't be printed. " ) ) ;
}
}
void PDFPageContentProcessor : : operatorTextNextLineShowText ( PDFOperandString text )
{
operatorTextMoveByLeading ( ) ;
operatorTextShowTextString ( qMove ( text ) ) ;
}
void PDFPageContentProcessor : : operatorTextSetSpacingAndShowText ( PDFReal t_w , PDFReal t_c , PDFOperandString text )
{
m_graphicState . setTextWordSpacing ( t_w ) ;
m_graphicState . setTextCharacterSpacing ( t_c ) ;
updateGraphicState ( ) ;
operatorTextNextLineShowText ( qMove ( text ) ) ;
}
2019-08-25 18:16:37 +02:00
void PDFPageContentProcessor : : operatorShadingPaintShape ( PDFPageContentProcessor : : PDFOperandName name )
{
2019-12-31 17:39:31 +01:00
if ( isContentKindSuppressed ( ContentKind : : Shading ) )
{
// Images are suppressed
return ;
}
2019-08-31 14:37:18 +02:00
QMatrix matrix = getCurrentWorldMatrix ( ) ;
2019-08-25 18:16:37 +02:00
PDFPageContentProcessorStateGuard guard ( this ) ;
PDFTemporaryValueChange guard2 ( & m_patternBaseMatrix , matrix ) ;
if ( ! m_shadingDictionary )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Shading '%1' not found. " ) . arg ( QString : : fromLatin1 ( name . name ) ) ) ;
}
2019-12-25 17:56:17 +01:00
PDFPatternPtr pattern = PDFPattern : : createShadingPattern ( m_colorSpaceDictionary , m_document , m_shadingDictionary - > get ( name . name ) , QMatrix ( ) , PDFObject ( ) , m_CMS , m_graphicState . getRenderingIntent ( ) , this , true ) ;
2019-08-25 18:16:37 +02:00
// We will do a trick: we will set current fill color space, and then paint
// bounding rectangle in the color pattern.
2019-09-26 19:14:04 +02:00
m_graphicState . setFillColorSpace ( PDFColorSpacePointer ( new PDFPatternColorSpace ( qMove ( pattern ) , nullptr , PDFColor ( ) ) ) ) ;
2019-08-25 18:16:37 +02:00
updateGraphicState ( ) ;
Q_ASSERT ( matrix . isInvertible ( ) ) ;
QMatrix inverted = matrix . inverted ( ) ;
QPainterPath deviceBoundingRectPath ;
deviceBoundingRectPath . addRect ( m_pageBoundingRectDeviceSpace ) ;
QPainterPath boundingRectPath = inverted . map ( deviceBoundingRectPath ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( boundingRectPath , false , true , false , boundingRectPath . fillRule ( ) ) ;
2019-08-25 18:16:37 +02:00
}
2019-07-21 17:31:39 +02:00
void PDFPageContentProcessor : : paintXObjectImage ( const PDFStream * stream )
{
2019-12-31 17:39:31 +01:00
if ( isContentKindSuppressed ( ContentKind : : Images ) )
{
// Images are suppressed
return ;
}
2019-07-21 17:31:39 +02:00
PDFColorSpacePointer colorSpace ;
const PDFDictionary * streamDictionary = stream - > getDictionary ( ) ;
if ( streamDictionary - > hasKey ( " ColorSpace " ) )
{
const PDFObject & colorSpaceObject = m_document - > getObject ( streamDictionary - > get ( " ColorSpace " ) ) ;
if ( colorSpaceObject . isName ( ) | | colorSpaceObject . isArray ( ) )
{
colorSpace = PDFAbstractColorSpace : : createColorSpace ( m_colorSpaceDictionary , m_document , colorSpaceObject ) ;
}
else if ( ! colorSpaceObject . isNull ( ) )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid color space of the image. " ) ) ;
}
}
2019-10-18 17:28:45 +02:00
PDFImage pdfImage = PDFImage : : createImage ( m_document , stream , qMove ( colorSpace ) , false , m_graphicState . getRenderingIntent ( ) , this ) ;
2019-12-25 17:56:17 +01:00
QImage image = pdfImage . getImage ( m_CMS , this ) ;
2019-07-21 17:31:39 +02:00
2019-07-24 19:15:03 +02:00
if ( image . format ( ) = = QImage : : Format_Alpha8 )
{
QSize size = image . size ( ) ;
QImage unmaskedImage ( size , QImage : : Format_ARGB32_Premultiplied ) ;
unmaskedImage . fill ( m_graphicState . getFillColor ( ) ) ;
unmaskedImage . setAlphaChannel ( image ) ;
image = qMove ( unmaskedImage ) ;
}
2019-07-21 17:31:39 +02:00
if ( ! image . isNull ( ) )
{
performImagePainting ( image ) ;
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Can't decode the image. " ) ) ;
}
}
2019-09-27 18:41:56 +02:00
void PDFPageContentProcessor : : reportWarningAboutColorOperatorsInUTP ( )
{
2019-10-05 13:08:32 +02:00
reportRenderErrorOnce ( RenderErrorType : : Warning , PDFTranslationContext : : tr ( " Color operators are not allowed in uncolored tilling pattern. " ) ) ;
2019-09-27 18:41:56 +02:00
}
2019-05-07 18:21:22 +02:00
void PDFPageContentProcessor : : operatorPaintXObject ( PDFPageContentProcessor : : PDFOperandName name )
{
2019-08-24 14:55:26 +02:00
// We want to have empty operands, when we are invoking forms
m_operands . clear ( ) ;
2019-05-07 18:21:22 +02:00
if ( m_xobjectDictionary )
{
const PDFObject & object = m_document - > getObject ( m_xobjectDictionary - > get ( name . name ) ) ;
if ( object . isStream ( ) )
{
const PDFStream * stream = object . getStream ( ) ;
const PDFDictionary * streamDictionary = stream - > getDictionary ( ) ;
2020-01-18 17:53:06 +01:00
// According to the specification, XObjects are skipped entirely, as no operator was invoked.
if ( streamDictionary - > hasKey ( " OC " ) )
{
const PDFObject & object = streamDictionary - > get ( " OC " ) ;
if ( object . isReference ( ) )
{
if ( isContentSuppressedByOC ( object . getReference ( ) ) )
{
return ;
}
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Reference to optional content expected. " ) ) ;
}
}
2019-05-07 18:21:22 +02:00
PDFDocumentDataLoaderDecorator loader ( m_document ) ;
QByteArray subtype = loader . readNameFromDictionary ( streamDictionary , " Subtype " ) ;
if ( subtype = = " Image " )
{
2019-07-21 17:31:39 +02:00
paintXObjectImage ( stream ) ;
2019-05-07 18:21:22 +02:00
}
2019-07-21 17:31:39 +02:00
else if ( subtype = = " Form " )
{
2019-06-16 16:32:23 +02:00
PDFInteger formType = loader . readIntegerFromDictionary ( streamDictionary , " FormType " , 1 ) ;
if ( formType ! = 1 )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Form of type %1 not supported. " ) . arg ( formType ) ) ;
}
// Read the bounding rectangle, if it is present
QRectF boundingBox = loader . readRectangle ( streamDictionary - > get ( " BBox " ) , QRectF ( ) ) ;
// Read the transformation matrix, if it is present
2019-08-25 18:16:37 +02:00
QMatrix transformationMatrix = loader . readMatrixFromDictionary ( streamDictionary , " Matrix " , QMatrix ( ) ) ;
2019-06-16 16:32:23 +02:00
// Read the dictionary content
QByteArray content = m_document - > getDecodedStream ( stream ) ;
// Read resources
PDFObject resources = m_document - > getObject ( streamDictionary - > get ( " Resources " ) ) ;
2019-09-29 18:09:09 +02:00
// Transparency group
PDFObject transparencyGroup = m_document - > getObject ( streamDictionary - > get ( " Group " ) ) ;
processForm ( transformationMatrix , boundingBox , resources , transparencyGroup , content ) ;
2019-07-21 17:31:39 +02:00
}
2019-05-07 18:21:22 +02:00
else
{
2019-06-16 16:32:23 +02:00
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Unknown XObject type '%1'. " ) . arg ( QString : : fromLatin1 ( subtype ) ) ) ;
2019-05-07 18:21:22 +02:00
}
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid format of XObject. Dictionary expected. " ) ) ;
}
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " XObject resource dictionary not found. " ) ) ;
}
}
2019-07-04 17:52:38 +02:00
void PDFPageContentProcessor : : operatorMarkedContentPoint ( PDFOperandName name )
{
performMarkedContentPoint ( name . name , PDFObject ( ) ) ;
}
void PDFPageContentProcessor : : operatorMarkedContentPointWithProperties ( PDFOperandName name , PDFObject properties )
{
performMarkedContentPoint ( name . name , properties ) ;
}
void PDFPageContentProcessor : : operatorMarkedContentBegin ( PDFOperandName name )
{
operatorMarkedContentBeginWithProperties ( qMove ( name ) , PDFObject ( ) ) ;
}
void PDFPageContentProcessor : : operatorMarkedContentBeginWithProperties ( PDFOperandName name , PDFObject properties )
{
// Handle the optional content
if ( name . name = = " OC " )
{
PDFObjectReference ocg ;
if ( m_propertiesDictionary & & properties . isName ( ) )
{
const PDFObject & ocgObject = m_propertiesDictionary - > get ( properties . getString ( ) ) ;
if ( ocgObject . isReference ( ) )
{
ocg = ocgObject . getReference ( ) ;
}
}
m_markedContentStack . emplace_back ( name . name , MarkedContentKind : : OptionalContent , isContentSuppressedByOC ( ocg ) ) ;
}
else
{
m_markedContentStack . emplace_back ( name . name , MarkedContentKind : : Other , false ) ;
}
performMarkedContentBegin ( name . name , properties ) ;
}
void PDFPageContentProcessor : : operatorMarkedContentEnd ( )
{
if ( m_markedContentStack . empty ( ) )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Mismatched begin/end of marked content. " ) ) ;
}
m_markedContentStack . pop_back ( ) ;
performMarkedContentEnd ( ) ;
}
2019-06-23 18:35:32 +02:00
void PDFPageContentProcessor : : operatorCompatibilityBegin ( )
{
+ + m_compatibilityBeginEndState ;
}
void PDFPageContentProcessor : : operatorCompatibilityEnd ( )
{
if ( - - m_compatibilityBeginEndState < 0 )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Compatibility operator begin/end mismatch. " ) ) ;
}
}
2019-03-30 18:45:30 +01:00
void PDFPageContentProcessor : : drawText ( const TextSequence & textSequence )
{
2019-03-31 14:17:01 +02:00
if ( textSequence . items . empty ( ) )
{
// Do not display empty text
return ;
}
2019-04-07 19:39:29 +02:00
const PDFRealizedFontPointer & font = getRealizedFont ( ) ;
if ( font )
2019-03-31 14:17:01 +02:00
{
2019-07-14 19:03:15 +02:00
const bool isType3Font = m_graphicState . getTextFont ( ) - > getFontType ( ) = = FontType : : Type3 ;
2019-04-07 19:39:29 +02:00
const PDFReal fontSize = m_graphicState . getTextFontSize ( ) ;
const PDFReal horizontalScaling = m_graphicState . getTextHorizontalScaling ( ) * 0.01 ; // Horizontal scaling is in percents
const PDFReal characterSpacing = m_graphicState . getTextCharacterSpacing ( ) ;
const PDFReal wordSpacing = m_graphicState . getTextWordSpacing ( ) ;
const PDFReal textRise = m_graphicState . getTextRise ( ) ;
const TextRenderingMode textRenderingMode = m_graphicState . getTextRenderingMode ( ) ;
const bool fill = isTextRenderingModeFilled ( textRenderingMode ) ;
const bool stroke = isTextRenderingModeStroked ( textRenderingMode ) ;
const bool clipped = isTextRenderingModeClipped ( textRenderingMode ) ;
// Detect horizontal writing system
const bool isHorizontalWritingSystem = font - > isHorizontalWritingSystem ( ) ;
// Calculate text rendering matrix
QMatrix adjustMatrix ( horizontalScaling , 0.0 , 0.0 , 1.0 , 0.0 , textRise ) ;
QMatrix textMatrix = m_graphicState . getTextMatrix ( ) ;
2019-07-14 19:03:15 +02:00
if ( ! isType3Font )
2019-04-07 19:39:29 +02:00
{
2019-07-14 19:03:15 +02:00
for ( const TextSequenceItem & item : textSequence . items )
2019-04-07 19:39:29 +02:00
{
2019-07-14 19:03:15 +02:00
PDFReal displacementX = 0.0 ;
PDFReal displacementY = 0.0 ;
2019-04-07 19:39:29 +02:00
2019-07-14 19:03:15 +02:00
if ( item . isCharacter ( ) )
2019-04-07 19:39:29 +02:00
{
2019-07-14 19:03:15 +02:00
QChar character = item . character ;
QPointF advance = isHorizontalWritingSystem ? QPointF ( item . advance , 0 ) : QPointF ( 0 , item . advance ) ;
2019-04-07 19:39:29 +02:00
2019-07-14 19:03:15 +02:00
// First, compute the advance
const PDFReal additionalAdvance = ( character = = QChar ( QChar : : Space ) ) ? wordSpacing + characterSpacing : characterSpacing ;
if ( isHorizontalWritingSystem )
{
advance . rx ( ) + = additionalAdvance ;
}
else
2019-04-07 19:39:29 +02:00
{
2019-07-14 19:03:15 +02:00
advance . ry ( ) + = additionalAdvance ;
}
advance . rx ( ) * = horizontalScaling ;
2019-04-29 18:13:16 +02:00
2019-07-14 19:03:15 +02:00
// Then get the glyph path and paint it
if ( item . glyph )
{
const QPainterPath & glyphPath = * item . glyph ;
if ( ! glyphPath . isEmpty ( ) )
2019-04-29 18:13:16 +02:00
{
2019-07-14 19:03:15 +02:00
QMatrix textRenderingMatrix = adjustMatrix * textMatrix ;
2019-12-28 19:21:29 +01:00
QMatrix toDeviceSpaceTransform = textRenderingMatrix * m_graphicState . getCurrentTransformationMatrix ( ) ;
2019-07-14 19:03:15 +02:00
QPainterPath transformedGlyph = textRenderingMatrix . map ( glyphPath ) ;
2019-08-31 15:55:59 +02:00
processPathPainting ( transformedGlyph , stroke , fill , true , transformedGlyph . fillRule ( ) ) ;
2019-07-14 19:03:15 +02:00
2019-12-28 19:21:29 +01:00
if ( ! item . character . isNull ( ) & & ! item . character . isSpace ( ) )
{
// Output character
PDFTextCharacterInfo info ;
info . character = item . character ;
info . isVerticalWritingSystem = ! isHorizontalWritingSystem ;
info . advance = item . advance ;
info . fontSize = fontSize ;
info . outline = glyphPath ;
info . matrix = toDeviceSpaceTransform ;
performOutputCharacter ( info ) ;
}
2019-07-14 19:03:15 +02:00
if ( clipped )
{
// Clipping is enabled, we must transform to the device coordinates
m_textClippingPath = m_textClippingPath . united ( toDeviceSpaceTransform . map ( glyphPath ) ) ;
}
2019-04-29 18:13:16 +02:00
}
2019-04-07 19:39:29 +02:00
}
2019-07-14 19:03:15 +02:00
displacementX = advance . x ( ) ;
displacementY = advance . y ( ) ;
}
else if ( item . isAdvance ( ) )
{
if ( horizontalScaling )
{
displacementX = - item . advance * 0.001 * fontSize * horizontalScaling ;
}
else
{
displacementY = - item . advance * 0.001 * fontSize ;
}
2019-04-07 19:39:29 +02:00
}
2019-07-14 19:03:15 +02:00
textMatrix . translate ( displacementX , displacementY ) ;
2019-04-07 19:39:29 +02:00
}
2019-07-14 19:03:15 +02:00
}
else
{
// Type 3 Font
Q_ASSERT ( dynamic_cast < const PDFType3Font * > ( m_graphicState . getTextFont ( ) . get ( ) ) ) ;
const PDFType3Font * parentFont = static_cast < const PDFType3Font * > ( m_graphicState . getTextFont ( ) . get ( ) ) ;
QMatrix fontMatrix = parentFont - > getFontMatrix ( ) ;
2019-07-24 19:15:03 +02:00
if ( ! fontMatrix . isInvertible ( ) )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Type 3 font matrix is not invertible. " ) ) ;
}
PDFPageContentProcessorStateGuard guard ( this ) ;
QMatrix invertedFontMatrix = fontMatrix . inverted ( ) ;
2019-07-14 19:03:15 +02:00
PDFObject resources = parentFont - > getResources ( ) ;
if ( ! resources . isNull ( ) )
2019-04-07 19:39:29 +02:00
{
2019-07-14 19:03:15 +02:00
initDictionaries ( resources ) ;
}
2019-07-24 19:15:03 +02:00
QMatrix fontAdjustedMatrix = invertedFontMatrix * adjustMatrix ;
2019-07-14 19:03:15 +02:00
for ( const TextSequenceItem & item : textSequence . items )
{
// First, compute horizontal advance
qreal displacementX = 0.0 ;
if ( item . advance ! = 0.0 )
2019-04-07 19:39:29 +02:00
{
2019-07-14 19:03:15 +02:00
qreal ry = 0.0 ;
fontAdjustedMatrix . map ( item . advance , 0.0 , & displacementX , & ry ) ;
2019-04-07 19:39:29 +02:00
}
2019-07-14 19:03:15 +02:00
if ( item . characterContentStream )
2019-04-07 19:39:29 +02:00
{
2019-07-14 19:03:15 +02:00
PDFPageContentProcessorStateGuard guard ( this ) ;
// We must clear operands, because we are processing a new content stream
m_operands . clear ( ) ;
QMatrix worldMatrix = fontAdjustedMatrix * textMatrix * m_graphicState . getCurrentTransformationMatrix ( ) ;
m_graphicState . setCurrentTransformationMatrix ( worldMatrix ) ;
updateGraphicState ( ) ;
processContent ( * item . characterContentStream ) ;
2019-12-28 19:21:29 +01:00
if ( ! item . character . isNull ( ) & & ! item . character . isSpace ( ) )
{
// Output character
PDFTextCharacterInfo info ;
info . character = item . character ;
info . isVerticalWritingSystem = ! isHorizontalWritingSystem ;
info . advance = item . advance ;
info . fontSize = fontSize ;
info . matrix = worldMatrix ;
performOutputCharacter ( info ) ;
}
2019-04-07 19:39:29 +02:00
}
2019-07-14 19:03:15 +02:00
textMatrix . translate ( displacementX , 0.0 ) ;
}
2019-04-07 19:39:29 +02:00
}
m_graphicState . setTextMatrix ( textMatrix ) ;
updateGraphicState ( ) ;
2019-03-31 14:17:01 +02:00
}
else
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid font, text can't be printed. " ) ) ;
}
2019-03-30 18:45:30 +01:00
}
2019-10-02 19:37:19 +02:00
PDFRealizedFontPointer PDFPageContentProcessor : : getRealizedFontImpl ( )
2019-03-30 18:45:30 +01:00
{
if ( m_graphicState . getTextFont ( ) )
{
2019-10-02 19:37:19 +02:00
return m_fontCache - > getRealizedFont ( m_graphicState . getTextFont ( ) , m_graphicState . getTextFontSize ( ) , this ) ;
2019-03-30 18:45:30 +01:00
}
2019-04-07 19:39:29 +02:00
return PDFRealizedFontPointer ( ) ;
2019-03-30 18:45:30 +01:00
}
2019-04-29 14:14:06 +02:00
void PDFPageContentProcessor : : checkStrokingColor ( )
{
if ( ! m_graphicState . getStrokeColor ( ) . isValid ( ) )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid stroking color. " ) ) ;
}
}
void PDFPageContentProcessor : : checkFillingColor ( )
{
if ( ! m_graphicState . getFillColor ( ) . isValid ( ) )
{
throw PDFRendererException ( RenderErrorType : : Error , PDFTranslationContext : : tr ( " Invalid filling color. " ) ) ;
}
}
2019-07-04 17:52:38 +02:00
PDFObject PDFPageContentProcessor : : readObjectFromOperandStack ( size_t startPosition ) const
{
auto tokenFetcher = [ this , & startPosition ] ( )
{
if ( startPosition < m_operands . size ( ) )
{
return m_operands [ startPosition + + ] ;
}
return PDFLexicalAnalyzer : : Token ( ) ;
} ;
PDFParser parser ( tokenFetcher ) ;
return parser . getObject ( ) ;
}
bool PDFPageContentProcessor : : isContentSuppressedByOC ( PDFObjectReference ocgOrOcmd )
{
if ( ! m_optionalContentActivity )
{
// Optional content activity control is suppressed, treat all content as not suppressed
return false ;
}
if ( m_optionalContentActivity - > getProperties ( ) - > hasOptionalContentGroup ( ocgOrOcmd ) )
{
// Simplest case - we have single optional content group
return m_optionalContentActivity - > getState ( ocgOrOcmd ) = = OCState : : OFF ;
}
PDFOptionalContentMembershipObject ocmd ;
try
{
ocmd = PDFOptionalContentMembershipObject : : create ( m_document , PDFObject : : createReference ( ocgOrOcmd ) ) ;
}
2019-09-27 18:41:56 +02:00
catch ( PDFException e )
2019-07-04 17:52:38 +02:00
{
m_errorList . push_back ( PDFRenderError ( RenderErrorType : : Error , e . getMessage ( ) ) ) ;
}
if ( ocmd . isValid ( ) )
{
return ocmd . evaluate ( m_optionalContentActivity ) = = OCState : : OFF ;
}
return false ;
}
2019-02-24 17:48:37 +01:00
PDFPageContentProcessor : : PDFPageContentProcessorState : : PDFPageContentProcessorState ( ) :
m_currentTransformationMatrix ( ) ,
m_fillColorSpace ( ) ,
m_strokeColorSpace ( ) ,
m_fillColor ( Qt : : black ) ,
m_strokeColor ( Qt : : black ) ,
m_lineWidth ( 1.0 ) ,
m_lineCapStyle ( Qt : : FlatCap ) ,
m_lineJoinStyle ( Qt : : MiterJoin ) ,
m_mitterLimit ( 10.0 ) ,
2019-10-04 17:21:26 +02:00
m_renderingIntentName ( ) ,
2019-02-24 17:48:37 +01:00
m_flatness ( 1.0 ) ,
m_smoothness ( 0.01 ) ,
2019-03-25 18:44:45 +01:00
m_textCharacterSpacing ( 0.0 ) ,
m_textWordSpacing ( 0.0 ) ,
m_textHorizontalScaling ( 100.0 ) ,
m_textLeading ( 0.0 ) ,
m_textFontSize ( 0.0 ) ,
m_textRenderingMode ( TextRenderingMode : : Fill ) ,
m_textRise ( 0.0 ) ,
m_textKnockout ( true ) ,
2019-09-29 15:44:35 +02:00
m_alphaStroking ( 1.0 ) ,
m_alphaFilling ( 1.0 ) ,
m_blendMode ( BlendMode : : Normal ) ,
2019-10-04 17:21:26 +02:00
m_renderingIntent ( RenderingIntent : : Perceptual ) ,
m_overprintMode ( ) ,
m_alphaIsShape ( false ) ,
2019-02-24 17:48:37 +01:00
m_stateFlags ( StateUnchanged )
{
m_fillColorSpace . reset ( new PDFDeviceGrayColorSpace ) ;
m_strokeColorSpace = m_fillColorSpace ;
}
PDFPageContentProcessor : : PDFPageContentProcessorState : : ~ PDFPageContentProcessorState ( )
{
}
PDFPageContentProcessor : : PDFPageContentProcessorState & PDFPageContentProcessor : : PDFPageContentProcessorState : : operator = ( const PDFPageContentProcessor : : PDFPageContentProcessorState & other )
{
setCurrentTransformationMatrix ( other . getCurrentTransformationMatrix ( ) ) ;
setStrokeColorSpace ( other . m_strokeColorSpace ) ;
setFillColorSpace ( other . m_fillColorSpace ) ;
setStrokeColor ( other . getStrokeColor ( ) ) ;
setFillColor ( other . getFillColor ( ) ) ;
setLineWidth ( other . getLineWidth ( ) ) ;
setLineCapStyle ( other . getLineCapStyle ( ) ) ;
setLineJoinStyle ( other . getLineJoinStyle ( ) ) ;
setMitterLimit ( other . getMitterLimit ( ) ) ;
setLineDashPattern ( other . getLineDashPattern ( ) ) ;
2019-10-04 17:21:26 +02:00
setRenderingIntentName ( other . getRenderingIntentName ( ) ) ;
2019-02-24 17:48:37 +01:00
setFlatness ( other . getFlatness ( ) ) ;
setSmoothness ( other . getSmoothness ( ) ) ;
2019-03-25 18:44:45 +01:00
setTextCharacterSpacing ( other . getTextCharacterSpacing ( ) ) ;
setTextWordSpacing ( other . getTextWordSpacing ( ) ) ;
setTextHorizontalScaling ( other . getTextHorizontalScaling ( ) ) ;
setTextLeading ( other . getTextLeading ( ) ) ;
setTextFont ( other . getTextFont ( ) ) ;
setTextFontSize ( other . getTextFontSize ( ) ) ;
setTextRenderingMode ( other . getTextRenderingMode ( ) ) ;
setTextRise ( other . getTextRise ( ) ) ;
setTextKnockout ( other . getTextKnockout ( ) ) ;
setTextMatrix ( other . getTextMatrix ( ) ) ;
setTextLineMatrix ( other . getTextLineMatrix ( ) ) ;
2019-09-29 15:44:35 +02:00
setAlphaStroking ( other . getAlphaStroking ( ) ) ;
setAlphaFilling ( other . getAlphaFilling ( ) ) ;
setBlendMode ( other . getBlendMode ( ) ) ;
2019-10-04 17:21:26 +02:00
setRenderingIntent ( other . getRenderingIntent ( ) ) ;
setOverprintMode ( other . getOverprintMode ( ) ) ;
setAlphaIsShape ( other . getAlphaIsShape ( ) ) ;
2019-02-24 17:48:37 +01:00
return * this ;
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setCurrentTransformationMatrix ( const QMatrix & currentTransformationMatrix )
{
if ( m_currentTransformationMatrix ! = currentTransformationMatrix )
{
m_currentTransformationMatrix = currentTransformationMatrix ;
m_stateFlags | = StateCurrentTransformationMatrix ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setStrokeColorSpace ( const QSharedPointer < PDFAbstractColorSpace > & strokeColorSpace )
{
if ( m_strokeColorSpace ! = strokeColorSpace )
{
m_strokeColorSpace = strokeColorSpace ;
m_stateFlags | = StateStrokeColorSpace ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setFillColorSpace ( const QSharedPointer < PDFAbstractColorSpace > & fillColorSpace )
{
if ( m_fillColorSpace ! = fillColorSpace )
{
m_fillColorSpace = fillColorSpace ;
m_stateFlags | = StateFillColorSpace ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setStrokeColor ( const QColor & strokeColor )
{
if ( m_strokeColor ! = strokeColor )
{
m_strokeColor = strokeColor ;
m_stateFlags | = StateStrokeColor ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setFillColor ( const QColor & fillColor )
{
if ( m_fillColor ! = fillColor )
{
m_fillColor = fillColor ;
m_stateFlags | = StateFillColor ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setLineWidth ( PDFReal lineWidth )
{
if ( m_lineWidth ! = lineWidth )
{
m_lineWidth = lineWidth ;
m_stateFlags | = StateLineWidth ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setLineCapStyle ( Qt : : PenCapStyle lineCapStyle )
{
if ( m_lineCapStyle ! = lineCapStyle )
{
m_lineCapStyle = lineCapStyle ;
m_stateFlags | = StateLineCapStyle ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setLineJoinStyle ( Qt : : PenJoinStyle lineJoinStyle )
{
if ( m_lineJoinStyle ! = lineJoinStyle )
{
m_lineJoinStyle = lineJoinStyle ;
m_stateFlags | = StateLineJoinStyle ;
}
}
2019-03-25 18:44:45 +01:00
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setMitterLimit ( PDFReal mitterLimit )
2019-02-24 17:48:37 +01:00
{
if ( m_mitterLimit ! = mitterLimit )
{
m_mitterLimit = mitterLimit ;
m_stateFlags | = StateMitterLimit ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setLineDashPattern ( PDFLineDashPattern pattern )
{
if ( m_lineDashPattern ! = pattern )
{
m_lineDashPattern = std : : move ( pattern ) ;
m_stateFlags | = StateLineDashPattern ;
}
}
2019-10-04 17:21:26 +02:00
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setRenderingIntentName ( const QByteArray & renderingIntentName )
2019-02-24 17:48:37 +01:00
{
2019-10-04 17:21:26 +02:00
if ( m_renderingIntentName ! = renderingIntentName )
2019-02-24 17:48:37 +01:00
{
2019-10-04 17:21:26 +02:00
m_renderingIntentName = renderingIntentName ;
m_stateFlags | = StateRenderingIntentName ;
2019-02-24 17:48:37 +01:00
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setFlatness ( PDFReal flatness )
{
if ( m_flatness ! = flatness )
{
m_flatness = flatness ;
m_stateFlags | = StateFlatness ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setSmoothness ( PDFReal smoothness )
{
if ( m_smoothness ! = smoothness )
{
m_smoothness = smoothness ;
m_stateFlags | = StateSmoothness ;
}
}
2019-03-25 18:44:45 +01:00
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextLeading ( PDFReal textLeading )
{
if ( m_textLeading ! = textLeading )
{
m_textLeading = textLeading ;
m_stateFlags | = StateTextLeading ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextFontSize ( PDFReal textFontSize )
{
if ( m_textFontSize ! = textFontSize )
{
m_textFontSize = textFontSize ;
m_stateFlags | = StateTextFontSize ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextKnockout ( bool textKnockout )
{
if ( m_textKnockout ! = textKnockout )
{
m_textKnockout = textKnockout ;
m_stateFlags | = StateTextKnockout ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextLineMatrix ( const QMatrix & textLineMatrix )
{
if ( m_textLineMatrix ! = textLineMatrix )
{
m_textLineMatrix = textLineMatrix ;
m_stateFlags | = StateTextLineMatrix ;
}
}
2019-09-29 15:44:35 +02:00
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setAlphaStroking ( PDFReal alpha )
{
if ( m_alphaStroking ! = alpha )
{
m_alphaStroking = alpha ;
m_stateFlags | = StateAlphaStroking ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setAlphaFilling ( PDFReal alpha )
{
if ( m_alphaFilling ! = alpha )
{
m_alphaFilling = alpha ;
m_stateFlags | = StateAlphaFilling ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setBlendMode ( BlendMode mode )
{
if ( m_blendMode ! = mode )
{
m_blendMode = mode ;
m_stateFlags | = StateBlendMode ;
}
}
2019-10-04 17:21:26 +02:00
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setRenderingIntent ( RenderingIntent renderingIntent )
{
if ( m_renderingIntent ! = renderingIntent )
{
m_renderingIntent = renderingIntent ;
m_stateFlags | = StateRenderingIntent ;
}
}
2019-09-29 15:44:35 +02:00
QColor PDFPageContentProcessor : : PDFPageContentProcessorState : : getStrokeColorWithAlpha ( ) const
{
QColor color = getStrokeColor ( ) ;
color . setAlphaF ( m_alphaStroking ) ;
return color ;
}
QColor PDFPageContentProcessor : : PDFPageContentProcessorState : : getFillColorWithAlpha ( ) const
{
QColor color = getFillColor ( ) ;
color . setAlphaF ( m_alphaFilling ) ;
return color ;
}
2019-10-04 17:21:26 +02:00
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setOverprintMode ( PDFOverprintMode overprintMode )
{
if ( m_overprintMode ! = overprintMode )
{
m_overprintMode = overprintMode ;
m_stateFlags | = StateOverprint ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setAlphaIsShape ( bool alphaIsShape )
{
if ( m_alphaIsShape ! = alphaIsShape )
{
m_alphaIsShape = alphaIsShape ;
m_stateFlags | = StateAlphaIsShape ;
}
}
2019-03-25 18:44:45 +01:00
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextMatrix ( const QMatrix & textMatrix )
{
if ( m_textMatrix ! = textMatrix )
{
m_textMatrix = textMatrix ;
m_stateFlags | = StateTextMatrix ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextRise ( PDFReal textRise )
{
if ( m_textRise ! = textRise )
{
m_textRise = textRise ;
m_stateFlags | = StateTextRise ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextRenderingMode ( TextRenderingMode textRenderingMode )
{
if ( m_textRenderingMode ! = textRenderingMode )
{
m_textRenderingMode = textRenderingMode ;
m_stateFlags | = StateTextRenderingMode ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextFont ( const PDFFontPointer & textFont )
{
if ( m_textFont ! = textFont )
{
m_textFont = textFont ;
m_stateFlags | = StateTextFont ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextHorizontalScaling ( PDFReal textHorizontalScaling )
{
if ( m_textHorizontalScaling ! = textHorizontalScaling )
{
m_textHorizontalScaling = textHorizontalScaling ;
m_stateFlags | = StateTextHorizontalScaling ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextWordSpacing ( PDFReal textWordSpacing )
{
if ( m_textWordSpacing ! = textWordSpacing )
{
m_textWordSpacing = textWordSpacing ;
m_stateFlags | = StateTextWordSpacing ;
}
}
void PDFPageContentProcessor : : PDFPageContentProcessorState : : setTextCharacterSpacing ( PDFReal textCharacterSpacing )
{
if ( m_textCharacterSpacing ! = textCharacterSpacing )
{
m_textCharacterSpacing = textCharacterSpacing ;
m_stateFlags | = StateTextCharacterSpacing ;
}
}
2019-06-16 16:32:23 +02:00
PDFPageContentProcessor : : PDFPageContentProcessorStateGuard : : PDFPageContentProcessorStateGuard ( PDFPageContentProcessor * processor ) :
m_processor ( processor ) ,
m_colorSpaceDictionary ( processor - > m_colorSpaceDictionary ) ,
m_fontDictionary ( processor - > m_fontDictionary ) ,
m_xobjectDictionary ( processor - > m_xobjectDictionary ) ,
2019-07-04 17:52:38 +02:00
m_extendedGraphicStateDictionary ( processor - > m_extendedGraphicStateDictionary ) ,
2019-08-25 18:16:37 +02:00
m_propertiesDictionary ( processor - > m_propertiesDictionary ) ,
2019-09-13 16:28:20 +02:00
m_shadingDictionary ( processor - > m_shadingDictionary ) ,
m_patternDictionary ( processor - > m_patternDictionary )
2019-06-16 16:32:23 +02:00
{
m_processor - > operatorSaveGraphicState ( ) ;
}
PDFPageContentProcessor : : PDFPageContentProcessorStateGuard : : ~ PDFPageContentProcessorStateGuard ( )
{
// Restore dictionaries
m_processor - > m_colorSpaceDictionary = m_colorSpaceDictionary ;
m_processor - > m_fontDictionary = m_fontDictionary ;
m_processor - > m_xobjectDictionary = m_xobjectDictionary ;
m_processor - > m_extendedGraphicStateDictionary = m_extendedGraphicStateDictionary ;
2019-07-04 17:52:38 +02:00
m_processor - > m_propertiesDictionary = m_propertiesDictionary ;
2019-08-25 18:16:37 +02:00
m_processor - > m_shadingDictionary = m_shadingDictionary ;
2019-09-13 16:28:20 +02:00
m_processor - > m_patternDictionary = m_patternDictionary ;
2019-06-16 16:32:23 +02:00
m_processor - > operatorRestoreGraphicState ( ) ;
}
2019-09-29 18:09:09 +02:00
PDFPageContentProcessor : : PDFTransparencyGroupGuard : : PDFTransparencyGroupGuard ( PDFPageContentProcessor * processor , PDFTransparencyGroup & & group ) :
m_processor ( processor )
{
m_processor - > performBeginTransparencyGroup ( ProcessOrder : : BeforeOperation , group ) ;
m_processor - > m_transparencyGroupStack . push ( qMove ( group ) ) ;
m_processor - > performBeginTransparencyGroup ( ProcessOrder : : AfterOperation , m_processor - > m_transparencyGroupStack . top ( ) ) ;
}
PDFPageContentProcessor : : PDFTransparencyGroupGuard : : ~ PDFTransparencyGroupGuard ( )
{
m_processor - > performEndTransparencyGroup ( ProcessOrder : : BeforeOperation , m_processor - > m_transparencyGroupStack . top ( ) ) ;
PDFTransparencyGroup group = qMove ( m_processor - > m_transparencyGroupStack . top ( ) ) ;
m_processor - > m_transparencyGroupStack . pop ( ) ;
m_processor - > performEndTransparencyGroup ( ProcessOrder : : AfterOperation , group ) ;
}
2019-02-24 17:48:37 +01:00
} // namespace pdf