2024-04-01 16:38:02 +02:00
// Copyright (C) 2023-2024 Jakub Melka
2023-03-06 16:09:09 +01:00
//
// This file is part of PDF4QT.
//
// PDF4QT 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
// with the written consent of the copyright owner, any later version.
//
// PDF4QT 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 PDF4QT. If not, see <https://www.gnu.org/licenses/>.
# include "pdfpagecontenteditorprocessor.h"
2024-05-08 17:27:27 +02:00
# include "pdfdocumentbuilder.h"
2024-05-19 20:13:57 +02:00
# include "pdfobject.h"
# include "pdfstreamfilters.h"
2023-03-06 16:09:09 +01:00
2024-04-20 19:41:14 +02:00
# include <QStringBuilder>
2024-05-04 16:26:36 +02:00
# include <QXmlStreamReader>
2024-04-20 19:41:14 +02:00
2023-03-06 16:09:09 +01:00
namespace pdf
{
PDFPageContentEditorProcessor : : PDFPageContentEditorProcessor ( const PDFPage * page ,
const PDFDocument * document ,
const PDFFontCache * fontCache ,
const PDFCMS * CMS ,
const PDFOptionalContentActivity * optionalContentActivity ,
QTransform pagePointToDevicePointMatrix ,
const PDFMeshQualitySettings & meshQualitySettings ) :
BaseClass ( page , document , fontCache , CMS , optionalContentActivity , pagePointToDevicePointMatrix , meshQualitySettings )
{
m_clippingPaths . push ( QPainterPath ( ) ) ;
2024-04-28 19:50:55 +02:00
m_content . setFontDictionary ( * getFontDictionary ( ) ) ;
m_content . setXObjectDictionary ( * getXObjectDictionary ( ) ) ;
2023-03-06 16:09:09 +01:00
}
const PDFEditedPageContent & PDFPageContentEditorProcessor : : getEditedPageContent ( ) const
{
return m_content ;
}
PDFEditedPageContent PDFPageContentEditorProcessor : : takeEditedPageContent ( )
{
return std : : move ( m_content ) ;
}
void PDFPageContentEditorProcessor : : performInterceptInstruction ( Operator currentOperator ,
ProcessOrder processOrder ,
const QByteArray & operatorAsText )
{
2024-04-01 18:05:18 +02:00
BaseClass : : performInterceptInstruction ( currentOperator , processOrder , operatorAsText ) ;
2023-03-06 16:09:09 +01:00
if ( processOrder = = ProcessOrder : : BeforeOperation )
{
if ( currentOperator = = Operator : : TextBegin & & ! isTextProcessing ( ) )
{
2024-04-25 20:46:00 +02:00
m_contentElementText . reset ( new PDFEditedPageContentElementText ( * getGraphicState ( ) , getGraphicState ( ) - > getCurrentTransformationMatrix ( ) ) ) ;
2023-03-06 16:09:09 +01:00
}
}
else
{
if ( currentOperator = = Operator : : TextEnd & & ! isTextProcessing ( ) )
{
if ( m_contentElementText & & ! m_contentElementText - > isEmpty ( ) )
{
2024-04-01 16:38:02 +02:00
m_contentElementText - > setTextPath ( std : : move ( m_textPath ) ) ;
2024-05-04 16:26:36 +02:00
m_contentElementText - > setItemsAsText ( PDFEditedPageContentElementText : : createItemsAsText ( m_contentElementText - > getState ( ) , m_contentElementText - > getItems ( ) ) ) ;
2023-03-06 16:09:09 +01:00
m_content . addContentElement ( std : : move ( m_contentElementText ) ) ;
}
m_contentElementText . reset ( ) ;
m_textBoundingRect = QRectF ( ) ;
2024-04-01 16:38:02 +02:00
m_textPath = QPainterPath ( ) ;
2023-03-06 16:09:09 +01:00
}
}
}
void PDFPageContentEditorProcessor : : performPathPainting ( const QPainterPath & path , bool stroke , bool fill , bool text , Qt : : FillRule fillRule )
{
2024-04-01 18:05:18 +02:00
BaseClass : : performPathPainting ( path , stroke , fill , text , fillRule ) ;
2023-03-06 16:09:09 +01:00
if ( path . isEmpty ( ) )
{
return ;
}
if ( text )
{
2024-04-01 18:05:18 +02:00
QPainterPath mappedPath = getGraphicState ( ) - > getCurrentTransformationMatrix ( ) . map ( path ) ;
2023-03-06 16:09:09 +01:00
QRectF boundingRect = mappedPath . boundingRect ( ) ;
m_textBoundingRect = m_textBoundingRect . united ( boundingRect ) ;
2024-04-01 16:38:02 +02:00
m_textPath . addPath ( mappedPath ) ;
2023-03-06 16:09:09 +01:00
}
else
{
m_content . addContentPath ( * getGraphicState ( ) , path , stroke , fill ) ;
}
}
void PDFPageContentEditorProcessor : : performUpdateGraphicsState ( const PDFPageContentProcessorState & state )
{
2024-04-01 18:05:18 +02:00
BaseClass : : performUpdateGraphicsState ( state ) ;
2023-03-06 16:09:09 +01:00
if ( isTextProcessing ( ) & & m_contentElementText )
{
PDFEditedPageContentElementText : : Item item ;
item . isUpdateGraphicState = true ;
item . state = state ;
m_contentElementText - > addItem ( item ) ;
}
}
void PDFPageContentEditorProcessor : : performProcessTextSequence ( const TextSequence & textSequence , ProcessOrder order )
{
2024-04-01 18:05:18 +02:00
BaseClass : : performProcessTextSequence ( textSequence , order ) ;
2023-03-06 16:09:09 +01:00
if ( order = = ProcessOrder : : BeforeOperation )
{
PDFEditedPageContentElementText : : Item item ;
item . isText = true ;
item . textSequence = textSequence ;
m_contentElementText - > addItem ( item ) ;
}
}
bool PDFPageContentEditorProcessor : : performOriginalImagePainting ( const PDFImage & image , const PDFStream * stream )
{
2024-04-01 18:05:18 +02:00
BaseClass : : performOriginalImagePainting ( image , stream ) ;
2023-03-06 16:09:09 +01:00
PDFObject imageObject = PDFObject : : createStream ( std : : make_shared < PDFStream > ( * stream ) ) ;
2024-04-25 20:46:00 +02:00
m_content . addContentImage ( * getGraphicState ( ) , std : : move ( imageObject ) , QImage ( ) ) ;
2023-03-06 16:09:09 +01:00
return false ;
}
void PDFPageContentEditorProcessor : : performImagePainting ( const QImage & image )
{
2024-04-01 18:05:18 +02:00
BaseClass : : performImagePainting ( image ) ;
2023-03-06 16:09:09 +01:00
PDFEditedPageContentElement * backElement = m_content . getBackElement ( ) ;
Q_ASSERT ( backElement ) ;
PDFEditedPageContentElementImage * imageElement = backElement - > asImage ( ) ;
imageElement - > setImage ( image ) ;
}
void PDFPageContentEditorProcessor : : performSaveGraphicState ( ProcessOrder order )
{
2024-04-01 18:05:18 +02:00
BaseClass : : performSaveGraphicState ( order ) ;
2023-03-06 16:09:09 +01:00
if ( order = = ProcessOrder : : BeforeOperation )
{
m_clippingPaths . push ( m_clippingPaths . top ( ) ) ;
}
}
void PDFPageContentEditorProcessor : : performRestoreGraphicState ( ProcessOrder order )
{
2024-04-01 18:05:18 +02:00
BaseClass : : performRestoreGraphicState ( order ) ;
2023-03-06 16:09:09 +01:00
if ( order = = ProcessOrder : : AfterOperation )
{
m_clippingPaths . pop ( ) ;
}
}
void PDFPageContentEditorProcessor : : performClipping ( const QPainterPath & path , Qt : : FillRule fillRule )
{
2024-04-01 18:05:18 +02:00
BaseClass : : performClipping ( path , fillRule ) ;
2023-03-06 16:09:09 +01:00
if ( m_clippingPaths . top ( ) . isEmpty ( ) )
{
m_clippingPaths . top ( ) = path ;
}
else
{
m_clippingPaths . top ( ) = m_clippingPaths . top ( ) . intersected ( path ) ;
}
}
bool PDFPageContentEditorProcessor : : isContentKindSuppressed ( ContentKind kind ) const
{
switch ( kind )
{
case ContentKind : : Shading :
case ContentKind : : Tiling :
return true ;
default :
break ;
}
return false ;
}
QString PDFEditedPageContent : : getOperatorToString ( PDFPageContentProcessor : : Operator operatorValue )
{
switch ( operatorValue )
{
case pdf : : PDFPageContentProcessor : : Operator : : SetLineWidth :
return " set_line_width " ;
case pdf : : PDFPageContentProcessor : : Operator : : SetLineCap :
return " set_line_cap " ;
case pdf : : PDFPageContentProcessor : : Operator : : SetLineJoin :
return " set_line_join " ;
case pdf : : PDFPageContentProcessor : : Operator : : SetMitterLimit :
return " set_mitter_limit " ;
case pdf : : PDFPageContentProcessor : : Operator : : SetLineDashPattern :
return " set_line_dash_pattern " ;
case pdf : : PDFPageContentProcessor : : Operator : : SetRenderingIntent :
return " set_rendering_intent " ;
case pdf : : PDFPageContentProcessor : : Operator : : SetFlatness :
return " set_flatness " ;
case pdf : : PDFPageContentProcessor : : Operator : : SetGraphicState :
return " set_graphic_state " ;
case pdf : : PDFPageContentProcessor : : Operator : : SaveGraphicState :
return " save " ;
case pdf : : PDFPageContentProcessor : : Operator : : RestoreGraphicState :
return " restore " ;
case pdf : : PDFPageContentProcessor : : Operator : : AdjustCurrentTransformationMatrix :
return " set_cm " ;
case pdf : : PDFPageContentProcessor : : Operator : : MoveCurrentPoint :
return " move_to " ;
case pdf : : PDFPageContentProcessor : : Operator : : LineTo :
return " line_to " ;
case pdf : : PDFPageContentProcessor : : Operator : : Bezier123To :
return " cubic123_to " ;
case pdf : : PDFPageContentProcessor : : Operator : : Bezier23To :
return " cubic23_to " ;
case pdf : : PDFPageContentProcessor : : Operator : : Bezier13To :
return " cubic13_to " ;
case pdf : : PDFPageContentProcessor : : Operator : : EndSubpath :
return " close_path " ;
case pdf : : PDFPageContentProcessor : : Operator : : Rectangle :
return " rect " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathStroke :
return " path_stroke " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathCloseStroke :
return " path_close_and_stroke " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathFillWinding :
return " path_fill_winding " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathFillWinding2 :
return " path_fill_winding " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathFillEvenOdd :
return " path_fill_even_odd " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathFillStrokeWinding :
return " path_fill_stroke_winding " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathFillStrokeEvenOdd :
return " path_fill_stroke_even_odd " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathCloseFillStrokeWinding :
return " path_close_fill_stroke_winding " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathCloseFillStrokeEvenOdd :
return " path_close_fill_stroke_even_odd " ;
case pdf : : PDFPageContentProcessor : : Operator : : PathClear :
return " path_clear " ;
case pdf : : PDFPageContentProcessor : : Operator : : ClipWinding :
return " clip_winding " ;
case pdf : : PDFPageContentProcessor : : Operator : : ClipEvenOdd :
return " clip_even_odd " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextBegin :
return " text_begin " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextEnd :
return " text_end " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetCharacterSpacing :
return " set_char_spacing " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetWordSpacing :
return " set_word_spacing " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetHorizontalScale :
return " set_hor_scale " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetLeading :
return " set_leading " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetFontAndFontSize :
return " set_font " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetRenderMode :
return " set_text_render_mode " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetRise :
return " set_text_rise " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextMoveByOffset :
return " text_move_by_offset " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetLeadingAndMoveByOffset :
return " text_set_leading_and_move_by_offset " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetMatrix :
return " text_set_matrix " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextMoveByLeading :
return " text_move_by_leading " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextShowTextString :
return " text_show_string " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextShowTextIndividualSpacing :
return " text_show_string_with_spacing " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextNextLineShowText :
return " text_next_line_and_show_text " ;
case pdf : : PDFPageContentProcessor : : Operator : : TextSetSpacingAndShowText :
return " text_set_spacing_and_show_text " ;
case pdf : : PDFPageContentProcessor : : Operator : : Type3FontSetOffset :
return " text_t3_set_offset " ;
case pdf : : PDFPageContentProcessor : : Operator : : Type3FontSetOffsetAndBB :
return " text_t3_set_offset_and_bb " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetStrokingColorSpace :
return " set_stroke_color_space " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetFillingColorSpace :
return " set_filling_color_space " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetStrokingColor :
return " set_stroke_color " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetStrokingColorN :
return " set_stroke_color_n " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetFillingColor :
return " set_filling_color " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetFillingColorN :
return " set_filling_color_n " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetDeviceGrayStroking :
return " set_stroke_gray_cs " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetDeviceGrayFilling :
return " set_filling_gray_cs " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetDeviceRGBStroking :
return " set_stroke_rgb_cs " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetDeviceRGBFilling :
return " set_filling_rgb_cs " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetDeviceCMYKStroking :
return " set_stroke_cmyk_cs " ;
case pdf : : PDFPageContentProcessor : : Operator : : ColorSetDeviceCMYKFilling :
return " set_filling_cmyk_cs " ;
case pdf : : PDFPageContentProcessor : : Operator : : ShadingPaintShape :
return " shading_paint " ;
case pdf : : PDFPageContentProcessor : : Operator : : InlineImageBegin :
return " ib " ;
case pdf : : PDFPageContentProcessor : : Operator : : InlineImageData :
return " id " ;
case pdf : : PDFPageContentProcessor : : Operator : : InlineImageEnd :
return " ie " ;
case pdf : : PDFPageContentProcessor : : Operator : : PaintXObject :
return " paint_object " ;
case pdf : : PDFPageContentProcessor : : Operator : : MarkedContentPoint :
return " mc_point " ;
case pdf : : PDFPageContentProcessor : : Operator : : MarkedContentPointWithProperties :
return " mc_point_prop " ;
case pdf : : PDFPageContentProcessor : : Operator : : MarkedContentBegin :
return " mc_begin " ;
case pdf : : PDFPageContentProcessor : : Operator : : MarkedContentBeginWithProperties :
return " mc_begin_prop " ;
case pdf : : PDFPageContentProcessor : : Operator : : MarkedContentEnd :
return " mc_end " ;
case pdf : : PDFPageContentProcessor : : Operator : : CompatibilityBegin :
return " compat_begin " ;
case pdf : : PDFPageContentProcessor : : Operator : : CompatibilityEnd :
return " compat_end " ;
default :
break ;
}
return QString ( ) ;
}
QString PDFEditedPageContent : : getOperandName ( PDFPageContentProcessor : : Operator operatorValue , int operandIndex )
{
static const std : : map < std : : pair < PDFPageContentProcessor : : Operator , int > , QString > operands =
{
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : SetLineWidth , 0 ) , " lineWidth " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : SetLineCap , 0 ) , " lineCap " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : SetLineJoin , 0 ) , " lineJoin " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : SetMitterLimit , 0 ) , " mitterLimit " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : SetRenderingIntent , 0 ) , " renderingIntent " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : SetFlatness , 0 ) , " flatness " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : SetGraphicState , 0 ) , " graphicState " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : AdjustCurrentTransformationMatrix , 0 ) , " a " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : AdjustCurrentTransformationMatrix , 1 ) , " b " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : AdjustCurrentTransformationMatrix , 2 ) , " c " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : AdjustCurrentTransformationMatrix , 3 ) , " d " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : AdjustCurrentTransformationMatrix , 4 ) , " e " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : AdjustCurrentTransformationMatrix , 5 ) , " f " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : MoveCurrentPoint , 0 ) , " x " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : MoveCurrentPoint , 1 ) , " y " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : LineTo , 0 ) , " x " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : LineTo , 1 ) , " y " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier123To , 0 ) , " x1 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier123To , 1 ) , " y1 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier123To , 2 ) , " x2 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier123To , 3 ) , " y2 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier123To , 4 ) , " x3 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier123To , 5 ) , " y3 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier23To , 0 ) , " x2 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier23To , 1 ) , " y2 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier23To , 2 ) , " x3 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier23To , 3 ) , " y3 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier13To , 0 ) , " x1 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier13To , 1 ) , " y1 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier13To , 2 ) , " x3 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Bezier13To , 3 ) , " y3 " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Rectangle , 0 ) , " x " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Rectangle , 1 ) , " y " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Rectangle , 2 ) , " width " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : Rectangle , 3 ) , " height " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetCharacterSpacing , 0 ) , " charSpacing " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetWordSpacing , 0 ) , " wordSpacing " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetHorizontalScale , 0 ) , " scale " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetLeading , 0 ) , " leading " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetFontAndFontSize , 0 ) , " font " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetFontAndFontSize , 1 ) , " fontSize " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetRenderMode , 0 ) , " renderMode " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetRise , 0 ) , " rise " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextMoveByOffset , 0 ) , " tx " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextMoveByOffset , 1 ) , " ty " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetLeadingAndMoveByOffset , 0 ) , " tx " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetLeadingAndMoveByOffset , 1 ) , " ty " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetMatrix , 0 ) , " a " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetMatrix , 1 ) , " b " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetMatrix , 2 ) , " c " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetMatrix , 3 ) , " d " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetMatrix , 4 ) , " e " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetMatrix , 5 ) , " f " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextShowTextString , 0 ) , " string " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextNextLineShowText , 0 ) , " string " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextShowTextIndividualSpacing , 0 ) , " wSpacing " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextShowTextIndividualSpacing , 1 ) , " chSpacing " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextShowTextIndividualSpacing , 2 ) , " string " } ,
{ std : : make_pair ( pdf : : PDFPageContentProcessor : : Operator : : TextSetSpacingAndShowText , 0 ) , " string " } ,
} ;
auto it = operands . find ( std : : make_pair ( operatorValue , operandIndex ) ) ;
if ( it ! = operands . cend ( ) )
{
return it - > second ;
}
return QString ( " op%1 " ) . arg ( operandIndex ) ;
}
void PDFEditedPageContent : : addContentPath ( PDFPageContentProcessorState state , QPainterPath path , bool strokePath , bool fillPath )
{
2024-04-25 20:46:00 +02:00
m_contentElements . emplace_back ( new PDFEditedPageContentElementPath ( std : : move ( state ) , std : : move ( path ) , strokePath , fillPath , state . getCurrentTransformationMatrix ( ) ) ) ;
2023-03-06 16:09:09 +01:00
}
2024-04-25 20:46:00 +02:00
void PDFEditedPageContent : : addContentImage ( PDFPageContentProcessorState state , PDFObject imageObject , QImage image )
2023-03-06 16:09:09 +01:00
{
2024-04-25 20:46:00 +02:00
m_contentElements . emplace_back ( new PDFEditedPageContentElementImage ( std : : move ( state ) , std : : move ( imageObject ) , std : : move ( image ) , state . getCurrentTransformationMatrix ( ) ) ) ;
2023-03-06 16:09:09 +01:00
}
void PDFEditedPageContent : : addContentElement ( std : : unique_ptr < PDFEditedPageContentElement > element )
{
m_contentElements . emplace_back ( std : : move ( element ) ) ;
}
PDFEditedPageContentElement * PDFEditedPageContent : : getBackElement ( ) const
{
if ( m_contentElements . empty ( ) )
{
return nullptr ;
}
return m_contentElements . back ( ) . get ( ) ;
}
2024-04-28 19:50:55 +02:00
PDFDictionary PDFEditedPageContent : : getFontDictionary ( ) const
{
return m_fontDictionary ;
}
void PDFEditedPageContent : : setFontDictionary ( const PDFDictionary & newFontDictionary )
{
m_fontDictionary = newFontDictionary ;
}
PDFDictionary PDFEditedPageContent : : getXObjectDictionary ( ) const
{
return m_xobjectDictionary ;
}
void PDFEditedPageContent : : setXObjectDictionary ( const PDFDictionary & newXobjectDictionary )
{
m_xobjectDictionary = newXobjectDictionary ;
}
2024-04-25 20:46:00 +02:00
PDFEditedPageContentElement : : PDFEditedPageContentElement ( PDFPageContentProcessorState state , QTransform transform ) :
m_state ( std : : move ( state ) ) ,
m_transform ( transform )
2023-03-06 16:09:09 +01:00
{
}
const PDFPageContentProcessorState & PDFEditedPageContentElement : : getState ( ) const
{
return m_state ;
}
void PDFEditedPageContentElement : : setState ( const PDFPageContentProcessorState & newState )
{
m_state = newState ;
}
2024-04-25 20:46:00 +02:00
QTransform PDFEditedPageContentElement : : getTransform ( ) const
{
return m_transform ;
}
void PDFEditedPageContentElement : : setTransform ( const QTransform & newTransform )
{
m_transform = newTransform ;
}
PDFEditedPageContentElementPath : : PDFEditedPageContentElementPath ( PDFPageContentProcessorState state , QPainterPath path , bool strokePath , bool fillPath , QTransform transform ) :
PDFEditedPageContentElement ( std : : move ( state ) , transform ) ,
2023-03-06 16:09:09 +01:00
m_path ( std : : move ( path ) ) ,
m_strokePath ( strokePath ) ,
m_fillPath ( fillPath )
{
}
PDFEditedPageContentElement : : Type PDFEditedPageContentElementPath : : getType ( ) const
{
return Type : : Path ;
}
PDFEditedPageContentElementPath * PDFEditedPageContentElementPath : : clone ( ) const
{
2024-04-25 20:46:00 +02:00
return new PDFEditedPageContentElementPath ( getState ( ) , getPath ( ) , getStrokePath ( ) , getFillPath ( ) , getTransform ( ) ) ;
2023-03-06 16:09:09 +01:00
}
QRectF PDFEditedPageContentElementPath : : getBoundingBox ( ) const
{
QPainterPath mappedPath = getState ( ) . getCurrentTransformationMatrix ( ) . map ( m_path ) ;
return mappedPath . boundingRect ( ) ;
}
QPainterPath PDFEditedPageContentElementPath : : getPath ( ) const
{
return m_path ;
}
void PDFEditedPageContentElementPath : : setPath ( QPainterPath newPath )
{
m_path = newPath ;
}
bool PDFEditedPageContentElementPath : : getStrokePath ( ) const
{
return m_strokePath ;
}
void PDFEditedPageContentElementPath : : setStrokePath ( bool newStrokePath )
{
m_strokePath = newStrokePath ;
}
bool PDFEditedPageContentElementPath : : getFillPath ( ) const
{
return m_fillPath ;
}
void PDFEditedPageContentElementPath : : setFillPath ( bool newFillPath )
{
m_fillPath = newFillPath ;
}
2024-04-25 20:46:00 +02:00
PDFEditedPageContentElementImage : : PDFEditedPageContentElementImage ( PDFPageContentProcessorState state , PDFObject imageObject , QImage image , QTransform transform ) :
PDFEditedPageContentElement ( std : : move ( state ) , transform ) ,
2023-03-06 16:09:09 +01:00
m_imageObject ( std : : move ( imageObject ) ) ,
2024-04-25 20:46:00 +02:00
m_image ( std : : move ( image ) )
2023-03-06 16:09:09 +01:00
{
}
PDFEditedPageContentElement : : Type PDFEditedPageContentElementImage : : getType ( ) const
{
return PDFEditedPageContentElement : : Type : : Image ;
}
PDFEditedPageContentElementImage * PDFEditedPageContentElementImage : : clone ( ) const
{
2024-04-25 20:46:00 +02:00
return new PDFEditedPageContentElementImage ( getState ( ) , getImageObject ( ) , getImage ( ) , getTransform ( ) ) ;
2024-04-01 16:38:02 +02:00
}
QRectF PDFEditedPageContentElementImage : : getBoundingBox ( ) const
{
2024-04-25 20:46:00 +02:00
return getTransform ( ) . mapRect ( QRectF ( 0 , 0 , 1 , 1 ) ) ;
2023-03-06 16:09:09 +01:00
}
PDFObject PDFEditedPageContentElementImage : : getImageObject ( ) const
{
return m_imageObject ;
}
void PDFEditedPageContentElementImage : : setImageObject ( const PDFObject & newImageObject )
{
m_imageObject = newImageObject ;
}
QImage PDFEditedPageContentElementImage : : getImage ( ) const
{
return m_image ;
}
void PDFEditedPageContentElementImage : : setImage ( const QImage & newImage )
{
m_image = newImage ;
}
2024-04-25 20:46:00 +02:00
PDFEditedPageContentElementText : : PDFEditedPageContentElementText ( PDFPageContentProcessorState state , QTransform transform ) :
PDFEditedPageContentElement ( state , transform )
2023-03-06 16:09:09 +01:00
{
}
2024-04-01 16:38:02 +02:00
PDFEditedPageContentElementText : : PDFEditedPageContentElementText ( PDFPageContentProcessorState state ,
std : : vector < Item > items ,
QPainterPath textPath ,
2024-05-04 16:26:36 +02:00
QTransform transform ,
QString itemsAsText ) :
2024-04-25 20:46:00 +02:00
PDFEditedPageContentElement ( state , transform ) ,
2024-04-01 16:38:02 +02:00
m_items ( std : : move ( items ) ) ,
2024-05-04 16:26:36 +02:00
m_textPath ( std : : move ( textPath ) ) ,
m_itemsAsText ( itemsAsText )
2023-03-06 16:09:09 +01:00
{
}
PDFEditedPageContentElement : : Type PDFEditedPageContentElementText : : getType ( ) const
{
return Type : : Text ;
}
PDFEditedPageContentElementText * PDFEditedPageContentElementText : : clone ( ) const
{
2024-05-04 16:26:36 +02:00
return new PDFEditedPageContentElementText ( getState ( ) , getItems ( ) , getTextPath ( ) , getTransform ( ) , getItemsAsText ( ) ) ;
2023-03-06 16:09:09 +01:00
}
void PDFEditedPageContentElementText : : addItem ( Item item )
{
m_items . emplace_back ( std : : move ( item ) ) ;
}
const std : : vector < PDFEditedPageContentElementText : : Item > & PDFEditedPageContentElementText : : getItems ( ) const
{
return m_items ;
}
void PDFEditedPageContentElementText : : setItems ( const std : : vector < Item > & newItems )
{
m_items = newItems ;
}
2024-04-01 16:38:02 +02:00
QRectF PDFEditedPageContentElementText : : getBoundingBox ( ) const
{
2024-04-25 20:46:00 +02:00
return getTransform ( ) . mapRect ( m_textPath . boundingRect ( ) ) ;
2024-04-01 16:38:02 +02:00
}
QPainterPath PDFEditedPageContentElementText : : getTextPath ( ) const
{
return m_textPath ;
}
void PDFEditedPageContentElementText : : setTextPath ( QPainterPath newTextPath )
{
m_textPath = newTextPath ;
}
2024-05-04 16:26:36 +02:00
QString PDFEditedPageContentElementText : : createItemsAsText ( const PDFPageContentProcessorState & initialState ,
const std : : vector < Item > & items )
2024-04-20 19:41:14 +02:00
{
QString text ;
2024-05-04 16:26:36 +02:00
PDFPageContentProcessorState state = initialState ;
2024-04-20 19:41:14 +02:00
state . setStateFlags ( PDFPageContentProcessorState : : StateFlags ( ) ) ;
2024-05-04 16:26:36 +02:00
for ( const Item & item : items )
2024-04-20 19:41:14 +02:00
{
if ( item . isText )
{
for ( const TextSequenceItem & textItem : item . textSequence . items )
{
if ( textItem . isCharacter ( ) )
{
if ( ! textItem . character . isNull ( ) )
{
text + = QString ( textItem . character ) . toHtmlEscaped ( ) ;
}
else if ( textItem . isAdvance ( ) )
{
text + = QString ( " <space advance= \" %1 \" /> " ) . arg ( textItem . advance ) ;
}
else if ( textItem . cid ! = 0 )
{
text + = QString ( " <character cid= \" %1 \" /> " ) . arg ( textItem . cid ) ;
}
}
}
}
else if ( item . isUpdateGraphicState )
{
PDFPageContentProcessorState newState = state ;
newState . setStateFlags ( PDFPageContentProcessorState : : StateFlags ( ) ) ;
newState . setState ( item . state ) ;
PDFPageContentProcessorState : : StateFlags flags = newState . getStateFlags ( ) ;
if ( flags . testFlag ( PDFPageContentProcessorState : : StateTextRenderingMode ) )
{
text + = QString ( " <tr v= \" %1 \" /> " ) . arg ( int ( newState . getTextRenderingMode ( ) ) ) ;
}
if ( flags . testFlag ( PDFPageContentProcessorState : : StateTextRise ) )
{
text + = QString ( " <ts v= \" %1 \" /> " ) . arg ( newState . getTextRise ( ) ) ;
}
if ( flags . testFlag ( PDFPageContentProcessorState : : StateTextCharacterSpacing ) )
{
text + = QString ( " <tc v= \" %1 \" /> " ) . arg ( newState . getTextCharacterSpacing ( ) ) ;
}
if ( flags . testFlag ( PDFPageContentProcessorState : : StateTextWordSpacing ) )
{
text + = QString ( " <tw v= \" %1 \" /> " ) . arg ( newState . getTextWordSpacing ( ) ) ;
}
if ( flags . testFlag ( PDFPageContentProcessorState : : StateTextLeading ) )
{
text + = QString ( " <tl v= \" %1 \" /> " ) . arg ( newState . getTextLeading ( ) ) ;
}
if ( flags . testFlag ( PDFPageContentProcessorState : : StateTextHorizontalScaling ) )
{
text + = QString ( " <tz v= \" %1 \" /> " ) . arg ( newState . getTextHorizontalScaling ( ) ) ;
}
if ( flags . testFlag ( PDFPageContentProcessorState : : StateTextKnockout ) )
{
text + = QString ( " <tk v= \" %1 \" /> " ) . arg ( newState . getTextKnockout ( ) ) ;
}
if ( flags . testFlag ( PDFPageContentProcessorState : : StateTextFont ) | |
flags . testFlag ( PDFPageContentProcessorState : : StateTextFontSize ) )
{
text + = QString ( " <tf font= \" %1 \" size= \" %2 \" /> " ) . arg ( newState . getTextFont ( ) - > getFontId ( ) ) . arg ( newState . getTextFontSize ( ) ) ;
}
if ( flags . testFlag ( PDFPageContentProcessorState : : StateTextMatrix ) )
{
QTransform transform = newState . getTextMatrix ( ) ;
qreal x = transform . dx ( ) ;
qreal y = transform . dy ( ) ;
if ( transform . isTranslating ( ) )
{
text + = QString ( " <tpos x= \" %1 \" y= \" %2 \" /> " ) . arg ( x ) . arg ( y ) ;
}
else
{
text + = QString ( " <tmatrix m11= \" %1 \" m12= \" %2 \" m21= \" %3 \" m22= \" %4 \" x= \" %5 \" y= \" %6 \" /> " ) . arg ( transform . m11 ( ) ) . arg ( transform . m12 ( ) ) . arg ( transform . m21 ( ) ) . arg ( transform . m22 ( ) ) . arg ( x ) . arg ( y ) ;
}
}
state = newState ;
state . setStateFlags ( PDFPageContentProcessorState : : StateFlags ( ) ) ;
}
}
return text ;
}
2024-05-04 16:26:36 +02:00
QString PDFEditedPageContentElementText : : getItemsAsText ( ) const
{
return m_itemsAsText ;
}
void PDFEditedPageContentElementText : : setItemsAsText ( const QString & newItemsAsText )
{
m_itemsAsText = newItemsAsText ;
}
2024-05-12 17:37:51 +02:00
void PDFPageContentEditorContentStreamBuilder : : writeStateDifference ( QTextStream & stream , const PDFPageContentProcessorState & state )
2024-04-28 19:50:55 +02:00
{
2024-05-12 17:37:51 +02:00
m_currentState . setState ( state ) ;
2024-04-28 19:50:55 +02:00
2024-05-12 17:37:51 +02:00
auto stateFlags = m_currentState . getStateFlags ( ) ;
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateCurrentTransformationMatrix ) )
{
QTransform transform = m_currentState . getCurrentTransformationMatrix ( ) ;
PDFReal m11 = transform . m11 ( ) ;
PDFReal m12 = transform . m12 ( ) ;
PDFReal m21 = transform . m21 ( ) ;
PDFReal m22 = transform . m22 ( ) ;
PDFReal x = transform . dx ( ) ;
PDFReal y = transform . dy ( ) ;
stream < < m11 < < " " < < m12 < < " " < < m21 < < " " < < m22 < < " " < < x < < " " < < y < < " cm " < < Qt : : endl ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateLineWidth ) )
{
stream < < m_currentState . getLineWidth ( ) < < " w " < < Qt : : endl ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateLineCapStyle ) )
{
stream < < PDFPageContentProcessor : : convertPenCapStyleToLineCap ( m_currentState . getLineCapStyle ( ) ) < < " J " < < Qt : : endl ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateLineJoinStyle ) )
{
stream < < PDFPageContentProcessor : : convertPenJoinStyleToLineJoin ( m_currentState . getLineJoinStyle ( ) ) < < " j " < < Qt : : endl ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateMitterLimit ) )
{
stream < < m_currentState . getMitterLimit ( ) < < " M " < < Qt : : endl ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateLineDashPattern ) )
{
const PDFLineDashPattern & dashPattern = m_currentState . getLineDashPattern ( ) ;
if ( dashPattern . isSolid ( ) )
{
stream < < " [] 0 d " < < Qt : : endl ;
}
else
{
stream < < " [ " ;
for ( PDFReal arrayItem : dashPattern . getDashArray ( ) )
{
stream < < arrayItem < < " " ;
}
stream < < " ] " < < dashPattern . getDashOffset ( ) < < " d " ;
}
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateRenderingIntent ) )
{
switch ( m_currentState . getRenderingIntent ( ) )
{
case pdf : : RenderingIntent : : Perceptual :
stream < < " /Perceptual ri " < < Qt : : endl ;
break ;
case pdf : : RenderingIntent : : AbsoluteColorimetric :
stream < < " /AbsoluteColorimetric ri " < < Qt : : endl ;
break ;
case pdf : : RenderingIntent : : RelativeColorimetric :
stream < < " /RelativeColorimetric ri " < < Qt : : endl ;
break ;
case pdf : : RenderingIntent : : Saturation :
stream < < " /Saturation ri " < < Qt : : endl ;
break ;
default :
break ;
}
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateMitterLimit ) )
{
stream < < m_currentState . getMitterLimit ( ) < < " M " < < Qt : : endl ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateFlatness ) )
{
stream < < m_currentState . getFlatness ( ) < < " i " < < Qt : : endl ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateStrokeColor ) | |
stateFlags . testFlag ( PDFPageContentProcessorState : : StateStrokeColorSpace ) )
{
QColor color = m_currentState . getStrokeColor ( ) ;
const PDFAbstractColorSpace * strokeColorSpace = m_currentState . getStrokeColorSpace ( ) ;
if ( strokeColorSpace & & strokeColorSpace - > getColorSpace ( ) = = PDFAbstractColorSpace : : ColorSpace : : DeviceGray )
{
stream < < qGray ( color . rgb ( ) ) / 255.0 < < " G " < < Qt : : endl ;
}
else if ( strokeColorSpace & & strokeColorSpace - > getColorSpace ( ) = = PDFAbstractColorSpace : : ColorSpace : : DeviceCMYK )
{
const PDFColor & color = m_currentState . getStrokeColorOriginal ( ) ;
if ( color . size ( ) > = 4 )
{
stream < < color [ 0 ] < < " " < < color [ 1 ] < < " " < < color [ 2 ] < < " " < < color [ 3 ] < < " K " ;
}
}
else
{
stream < < color . redF ( ) < < " " < < color . greenF ( ) < < " " < < color . blueF ( ) < < " RG " ;
}
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateFillColor ) | |
stateFlags . testFlag ( PDFPageContentProcessorState : : StateFillColorSpace ) )
{
QColor color = m_currentState . getFillColor ( ) ;
const PDFAbstractColorSpace * fillColorSpace = m_currentState . getFillColorSpace ( ) ;
if ( fillColorSpace & & fillColorSpace - > getColorSpace ( ) = = PDFAbstractColorSpace : : ColorSpace : : DeviceGray )
{
stream < < qGray ( color . rgb ( ) ) / 255.0 < < " G " < < Qt : : endl ;
}
else if ( fillColorSpace & & fillColorSpace - > getColorSpace ( ) = = PDFAbstractColorSpace : : ColorSpace : : DeviceCMYK )
{
const PDFColor & color = m_currentState . getFillColorOriginal ( ) ;
if ( color . size ( ) > = 4 )
{
stream < < color [ 0 ] < < " " < < color [ 1 ] < < " " < < color [ 2 ] < < " " < < color [ 3 ] < < " K " ;
}
}
else
{
stream < < color . redF ( ) < < " " < < color . greenF ( ) < < " " < < color . blueF ( ) < < " RG " ;
}
}
m_currentState . setStateFlags ( PDFPageContentProcessorState : : StateFlags ( ) ) ;
PDFObjectFactory stateDictionary ;
stateDictionary . beginDictionary ( ) ;
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateSmoothness ) )
{
stateDictionary . beginDictionaryItem ( " SM " ) ;
stateDictionary < < m_currentState . getSmoothness ( ) ;
stateDictionary . endDictionaryItem ( ) ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateAlphaStroking ) )
{
stateDictionary . beginDictionaryItem ( " CA " ) ;
stateDictionary < < m_currentState . getAlphaStroking ( ) ;
stateDictionary . endDictionaryItem ( ) ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateAlphaFilling ) )
{
stateDictionary . beginDictionaryItem ( " ca " ) ;
stateDictionary < < m_currentState . getAlphaFilling ( ) ;
stateDictionary . endDictionaryItem ( ) ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateAlphaIsShape ) )
{
stateDictionary . beginDictionaryItem ( " AIS " ) ;
stateDictionary < < m_currentState . getAlphaIsShape ( ) ;
stateDictionary . endDictionaryItem ( ) ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateTextKnockout ) )
{
stateDictionary . beginDictionaryItem ( " TK " ) ;
stateDictionary < < m_currentState . getTextKnockout ( ) ;
stateDictionary . endDictionaryItem ( ) ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateStrokeAdjustment ) )
{
stateDictionary . beginDictionaryItem ( " SA " ) ;
stateDictionary < < m_currentState . getStrokeAdjustment ( ) ;
stateDictionary . endDictionaryItem ( ) ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateBlendMode ) )
{
QString blendModeName = PDFBlendModeInfo : : getBlendModeName ( m_currentState . getBlendMode ( ) ) ;
stateDictionary . beginDictionaryItem ( " BM " ) ;
2024-05-19 20:13:57 +02:00
stateDictionary < < WrapName ( blendModeName . toLatin1 ( ) ) ;
2024-05-12 17:37:51 +02:00
stateDictionary . endDictionaryItem ( ) ;
}
if ( stateFlags . testFlag ( PDFPageContentProcessorState : : StateOverprint ) )
{
PDFOverprintMode overprintMode = m_currentState . getOverprintMode ( ) ;
stateDictionary . beginDictionaryItem ( " OPM " ) ;
stateDictionary < < overprintMode . overprintMode ;
stateDictionary . endDictionaryItem ( ) ;
stateDictionary . beginDictionaryItem ( " OP " ) ;
stateDictionary < < overprintMode . overprintStroking ;
stateDictionary . endDictionaryItem ( ) ;
stateDictionary . beginDictionaryItem ( " op " ) ;
stateDictionary < < overprintMode . overprintFilling ;
stateDictionary . endDictionaryItem ( ) ;
}
stateDictionary . endDictionary ( ) ;
PDFObject stateObject = stateDictionary . takeObject ( ) ;
const PDFDictionary * dictionary = m_document - > getDictionaryFromObject ( stateObject ) ;
if ( dictionary & & dictionary - > getCount ( ) > 0 )
{
// Apply state
QByteArray key ;
for ( size_t i = 0 ; i < m_graphicStateDictionary . getCount ( ) ; + + i )
{
const PDFDictionary * currentDictionary = m_document - > getDictionaryFromObject ( m_graphicStateDictionary . getValue ( i ) ) ;
if ( * currentDictionary = = * dictionary )
{
key = m_graphicStateDictionary . getKey ( i ) . getString ( ) ;
break ;
}
}
if ( key . isEmpty ( ) )
{
int i = 0 ;
while ( true )
{
QByteArray currentKey = QString ( " s%1 " ) . arg ( + + i ) . toLatin1 ( ) ;
if ( ! m_graphicStateDictionary . hasKey ( currentKey ) )
{
2024-05-19 20:13:57 +02:00
m_graphicStateDictionary . addEntry ( PDFInplaceOrMemoryString ( currentKey ) , std : : move ( stateObject ) ) ;
2024-05-12 17:37:51 +02:00
key = currentKey ;
break ;
}
}
}
stream < < " / " < < key < < " gs " < < Qt : : endl ;
}
2024-04-28 19:50:55 +02:00
}
void PDFPageContentEditorContentStreamBuilder : : writeElement ( const PDFEditedPageContentElement * element )
{
PDFPageContentProcessorState state = element - > getState ( ) ;
state . setCurrentTransformationMatrix ( element - > getTransform ( ) ) ;
2024-05-04 16:26:36 +02:00
QTextStream stream ( & m_outputContent , QDataStream : : WriteOnly ) ;
2024-05-12 17:37:51 +02:00
writeStateDifference ( stream , state ) ;
2024-04-28 19:50:55 +02:00
if ( const PDFEditedPageContentElementImage * imageElement = element - > asImage ( ) )
{
QImage image = imageElement - > getImage ( ) ;
2024-05-04 16:26:36 +02:00
2024-05-19 20:13:57 +02:00
writeImage ( stream , image ) ;
2024-04-28 19:50:55 +02:00
}
if ( const PDFEditedPageContentElementPath * pathElement = element - > asPath ( ) )
{
const bool isStroking = pathElement - > getStrokePath ( ) ;
const bool isFilling = pathElement - > getFillPath ( ) ;
writePainterPath ( stream , pathElement - > getPath ( ) , isStroking , isFilling ) ;
}
2024-05-04 16:26:36 +02:00
if ( const PDFEditedPageContentElementText * textElement = element - > asText ( ) )
{
QString text = textElement - > getItemsAsText ( ) ;
if ( ! text . isEmpty ( ) )
{
writeText ( stream , text ) ;
}
}
2024-04-28 19:50:55 +02:00
stream < < Qt : : endl ;
}
2024-05-04 16:26:36 +02:00
void PDFPageContentEditorContentStreamBuilder : : writePainterPath ( QTextStream & stream ,
2024-04-28 19:50:55 +02:00
const QPainterPath & path ,
bool isStroking ,
bool isFilling )
{
const int elementCount = path . elementCount ( ) ;
for ( int i = 0 ; i < elementCount ; + + i )
{
QPainterPath : : Element element = path . elementAt ( i ) ;
switch ( element . type )
{
case QPainterPath : : MoveToElement :
stream < < element . x < < " " < < element . y < < " m " < < Qt : : endl ;
break ;
case QPainterPath : : LineToElement :
stream < < element . x < < " " < < element . y < < " l " < < Qt : : endl ;
break ;
case QPainterPath : : CurveToElement :
stream < < element . x < < " " < < element . y < < " c " < < Qt : : endl ;
break ;
case QPainterPath : : CurveToDataElement :
stream < < element . x < < " " < < element . y < < " " ;
break ;
default :
break ;
}
}
if ( isStroking & & ! isFilling )
{
stream < < " S " < < Qt : : endl ;
}
else if ( isStroking | | isFilling )
{
switch ( path . fillRule ( ) )
{
case Qt : : OddEvenFill :
if ( isFilling & & isStroking )
{
stream < < " B* " < < Qt : : endl ;
}
else
{
stream < < " f* " < < Qt : : endl ;
}
break ;
case Qt : : WindingFill :
if ( isFilling & & isStroking )
{
stream < < " B " < < Qt : : endl ;
}
else
{
stream < < " f " < < Qt : : endl ;
}
break ;
default :
break ;
}
}
else
{
stream < < " n " < < Qt : : endl ;
}
}
2024-05-04 16:26:36 +02:00
void PDFPageContentEditorContentStreamBuilder : : writeText ( QTextStream & stream , const QString & text )
{
stream < < " q BT " < < Qt : : endl ;
QXmlStreamReader reader ( text ) ;
2024-05-08 17:27:27 +02:00
m_textFont = m_currentState . getTextFont ( ) ;
2024-05-04 16:26:36 +02:00
auto isCommand = [ & reader ] ( const char * tag ) - > bool
{
QString tagString = reader . name ( ) . toString ( ) ;
QXmlStreamAttributes attributes = reader . attributes ( ) ;
return tagString = = " tr " & & attributes . size ( ) = = 1 & & attributes . hasAttribute ( " v " ) ;
} ;
while ( ! reader . atEnd ( ) & & ! reader . hasError ( ) )
{
reader . readNext ( ) ;
if ( reader . isStartElement ( ) )
{
QXmlStreamAttributes attributes = reader . attributes ( ) ;
if ( isCommand ( " tr " ) )
{
const QXmlStreamAttribute & attribute = attributes . front ( ) ;
bool ok = false ;
const int textRenderingMode = attribute . value ( ) . toInt ( & ok ) ;
if ( ! ok | | textRenderingMode < 0 | | textRenderingMode > 7 )
{
addError ( PDFTranslationContext : : tr ( " Invalid rendering mode '%1'. Valid values are 0-7. " ) . arg ( textRenderingMode ) ) ;
}
else
{
stream < < textRenderingMode < < " Tr " < < Qt : : endl ;
}
}
else if ( isCommand ( " ts " ) )
{
const QXmlStreamAttribute & attribute = attributes . front ( ) ;
bool ok = false ;
const double textRise = attribute . value ( ) . toDouble ( & ok ) ;
if ( ! ok )
{
addError ( PDFTranslationContext : : tr ( " Cannot convert text '%1' to number. " ) . arg ( attribute . value ( ) . toString ( ) ) ) ;
}
else
{
stream < < textRise < < " Ts " < < Qt : : endl ;
}
}
else if ( isCommand ( " tc " ) )
{
const QXmlStreamAttribute & attribute = attributes . front ( ) ;
bool ok = false ;
const double textCharacterSpacing = attribute . value ( ) . toDouble ( & ok ) ;
if ( ! ok )
{
addError ( PDFTranslationContext : : tr ( " Cannot convert text '%1' to number. " ) . arg ( attribute . value ( ) . toString ( ) ) ) ;
}
else
{
stream < < textCharacterSpacing < < " Tc " < < Qt : : endl ;
}
}
else if ( isCommand ( " tw " ) )
{
const QXmlStreamAttribute & attribute = attributes . front ( ) ;
bool ok = false ;
const double textWordSpacing = attribute . value ( ) . toDouble ( & ok ) ;
if ( ! ok )
{
addError ( PDFTranslationContext : : tr ( " Cannot convert text '%1' to number. " ) . arg ( attribute . value ( ) . toString ( ) ) ) ;
}
else
{
stream < < textWordSpacing < < " Tw " < < Qt : : endl ;
}
}
else if ( isCommand ( " tl " ) )
{
const QXmlStreamAttribute & attribute = attributes . front ( ) ;
bool ok = false ;
const double textLeading = attribute . value ( ) . toDouble ( & ok ) ;
if ( ! ok )
{
addError ( PDFTranslationContext : : tr ( " Cannot convert text '%1' to number. " ) . arg ( attribute . value ( ) . toString ( ) ) ) ;
}
else
{
stream < < textLeading < < " TL " < < Qt : : endl ;
}
}
else if ( isCommand ( " tz " ) )
{
const QXmlStreamAttribute & attribute = attributes . front ( ) ;
bool ok = false ;
const PDFReal textScaling = attribute . value ( ) . toDouble ( & ok ) ;
if ( ! ok )
{
addError ( PDFTranslationContext : : tr ( " Cannot convert text '%1' to number. " ) . arg ( attribute . value ( ) . toString ( ) ) ) ;
}
else
{
stream < < textScaling < < " Tz " < < Qt : : endl ;
}
}
else if ( reader . name ( ) . toString ( ) = = " tf " )
{
if ( attributes . hasAttribute ( " font " ) & & attributes . hasAttribute ( " size " ) )
{
bool ok = false ;
2024-05-12 17:37:51 +02:00
QByteArray v1 = attributes . value ( " font " ) . toString ( ) . toLatin1 ( ) ;
2024-05-04 16:26:36 +02:00
PDFReal v2 = attributes . value ( " size " ) . toDouble ( & ok ) ;
if ( ! ok )
{
addError ( PDFTranslationContext : : tr ( " Cannot convert text '%1' to number. " ) . arg ( attributes . value ( " size " ) . toString ( ) ) ) ;
}
else
{
2024-05-08 17:27:27 +02:00
v1 = selectFont ( v1 ) ;
2024-05-04 16:26:36 +02:00
stream < < " / " < < v1 < < " " < < v2 < < " Tf " < < Qt : : endl ;
}
}
else
{
addError ( PDFTranslationContext : : tr ( " Text font command requires two attributes - font and size. " ) ) ;
}
}
else if ( reader . name ( ) . toString ( ) = = " tpos " )
{
if ( attributes . hasAttribute ( " x " ) & & attributes . hasAttribute ( " y " ) )
{
bool ok1 = false ;
bool ok2 = false ;
PDFReal v1 = attributes . value ( " x " ) . toDouble ( & ok1 ) ;
PDFReal v2 = attributes . value ( " y " ) . toDouble ( & ok2 ) ;
if ( ! ok1 )
{
addError ( PDFTranslationContext : : tr ( " Cannot convert text '%1' to number. " ) . arg ( attributes . value ( " x " ) . toString ( ) ) ) ;
}
else if ( ! ok2 )
{
addError ( PDFTranslationContext : : tr ( " Cannot convert text '%1' to number. " ) . arg ( attributes . value ( " y " ) . toString ( ) ) ) ;
}
else
{
stream < < v1 < < " " < < v2 < < " Td " < < Qt : : endl ;
}
}
else
{
addError ( PDFTranslationContext : : tr ( " Text translation command requires two attributes - x and y. " ) ) ;
}
}
else if ( reader . name ( ) . toString ( ) = = " tmatrix " )
{
if ( attributes . hasAttribute ( " m11 " ) & & attributes . hasAttribute ( " m12 " ) & &
attributes . hasAttribute ( " m21 " ) & & attributes . hasAttribute ( " m22 " ) & &
attributes . hasAttribute ( " x " ) & & attributes . hasAttribute ( " y " ) )
{
bool ok1 = false ;
bool ok2 = false ;
bool ok3 = false ;
bool ok4 = false ;
bool ok5 = false ;
bool ok6 = false ;
PDFReal m11 = attributes . value ( " m11 " ) . toDouble ( & ok1 ) ;
PDFReal m12 = attributes . value ( " m12 " ) . toDouble ( & ok2 ) ;
PDFReal m21 = attributes . value ( " m21 " ) . toDouble ( & ok3 ) ;
PDFReal m22 = attributes . value ( " m22 " ) . toDouble ( & ok4 ) ;
PDFReal x = attributes . value ( " x " ) . toDouble ( & ok5 ) ;
PDFReal y = attributes . value ( " y " ) . toDouble ( & ok6 ) ;
if ( ! ok1 | | ! ok2 | | ! ok3 | | ! ok4 | | ! ok5 | ! ok6 )
{
addError ( PDFTranslationContext : : tr ( " Invalid text matrix parameters. " ) ) ;
}
else
{
stream < < m11 < < " " < < m12 < < " " < < m21 < < " " < < m22 < < " " < < x < < " " < < y < < " Tm " < < Qt : : endl ;
}
}
else
{
addError ( PDFTranslationContext : : tr ( " Set text matrix command requires six elements - m11, m12, m21, m22, x, y. " ) ) ;
}
}
else
{
addError ( PDFTranslationContext : : tr ( " Invalid command '%1'. " ) . arg ( reader . name ( ) . toString ( ) ) ) ;
}
}
if ( reader . isCharacters ( ) )
{
QString characters = reader . text ( ) . toString ( ) ;
2024-05-08 17:27:27 +02:00
if ( m_textFont )
{
PDFEncodedText encodedText = m_textFont - > encodeText ( characters ) ;
if ( ! encodedText . encodedText . isEmpty ( ) )
{
stream < < " < " < < encodedText . encodedText . toHex ( ) < < " > Tj " < < Qt : : endl ;
}
if ( ! encodedText . isValid )
{
addError ( PDFTranslationContext : : tr ( " Error during converting text to font encoding. Some characters were not converted: '%1'. " ) . arg ( encodedText . errorString ) ) ;
}
}
else
{
addError ( PDFTranslationContext : : tr ( " Text font not defined! " ) ) ;
}
2024-05-04 16:26:36 +02:00
}
}
stream < < " ET Q " < < Qt : : endl ;
}
2024-05-19 20:13:57 +02:00
void PDFPageContentEditorContentStreamBuilder : : writeImage ( QTextStream & stream , const QImage & image )
{
QByteArray key ;
int i = 0 ;
while ( true )
{
QByteArray currentKey = QString ( " Im%1 " ) . arg ( + + i ) . toLatin1 ( ) ;
if ( ! m_xobjectDictionary . hasKey ( currentKey ) )
{
PDFArray array ;
array . appendItem ( PDFObject : : createName ( " FlateDecode " ) ) ;
QImage codedImage = image ;
codedImage = codedImage . convertToFormat ( QImage : : Format_ARGB32 ) ;
QByteArray decodedStream ( reinterpret_cast < const char * > ( image . constBits ( ) ) , image . sizeInBytes ( ) ) ;
// Compress the content stream
QByteArray compressedData = PDFFlateDecodeFilter : : compress ( decodedStream ) ;
PDFDictionary imageDictionary ;
imageDictionary . setEntry ( PDFInplaceOrMemoryString ( " Subtitle " ) , PDFObject : : createName ( " Image " ) ) ;
imageDictionary . setEntry ( PDFInplaceOrMemoryString ( " Width " ) , PDFObject : : createInteger ( image . width ( ) ) ) ;
imageDictionary . setEntry ( PDFInplaceOrMemoryString ( " Height " ) , PDFObject : : createInteger ( image . height ( ) ) ) ;
imageDictionary . setEntry ( PDFInplaceOrMemoryString ( " ColorSpace " ) , PDFObject : : createName ( " DeviceRGB " ) ) ;
imageDictionary . setEntry ( PDFInplaceOrMemoryString ( " BitsPerComponent " ) , PDFObject : : createInteger ( 8 ) ) ;
imageDictionary . setEntry ( PDFInplaceOrMemoryString ( " Length " ) , PDFObject : : createInteger ( compressedData . size ( ) ) ) ;
imageDictionary . setEntry ( PDFInplaceOrMemoryString ( " Filter " ) , PDFObject : : createArray ( std : : make_shared < PDFArray > ( qMove ( array ) ) ) ) ;
PDFObject imageObject = PDFObject : : createStream ( std : : make_shared < PDFStream > ( qMove ( imageDictionary ) , qMove ( compressedData ) ) ) ;
m_xobjectDictionary . addEntry ( PDFInplaceOrMemoryString ( currentKey ) , std : : move ( imageObject ) ) ;
key = currentKey ;
break ;
}
}
stream < < " / " < < key < < " Do " < < Qt : : endl ;
}
2024-05-08 17:27:27 +02:00
QByteArray PDFPageContentEditorContentStreamBuilder : : selectFont ( const QByteArray & font )
{
m_textFont = nullptr ;
PDFObject fontObject = m_fontDictionary . get ( font ) ;
if ( ! fontObject . isNull ( ) )
{
try
{
m_textFont = PDFFont : : createFont ( fontObject , font , m_document ) ;
}
catch ( const PDFException & )
{
addError ( PDFTranslationContext : : tr ( " Font '%1' is invalid. " ) . arg ( QString : : fromLatin1 ( font ) ) ) ;
}
}
if ( ! m_textFont )
{
QByteArray defaultFontKey = " PDF4QT_DefFnt " ;
if ( ! m_fontDictionary . hasKey ( defaultFontKey ) )
{
PDFObjectFactory defaultFontFactory ;
defaultFontFactory . beginDictionary ( ) ;
defaultFontFactory . beginDictionaryItem ( " Type " ) ;
defaultFontFactory < < WrapName ( " Font " ) ;
defaultFontFactory . endDictionaryItem ( ) ;
defaultFontFactory . beginDictionaryItem ( " Subtype " ) ;
defaultFontFactory < < WrapName ( " Type1 " ) ;
defaultFontFactory . endDictionaryItem ( ) ;
defaultFontFactory . beginDictionaryItem ( " BaseFont " ) ;
defaultFontFactory < < WrapName ( " Helvetica " ) ;
defaultFontFactory . endDictionaryItem ( ) ;
defaultFontFactory . beginDictionaryItem ( " Encoding " ) ;
defaultFontFactory < < WrapName ( " WinAnsiEncoding " ) ;
defaultFontFactory . endDictionaryItem ( ) ;
defaultFontFactory . endDictionary ( ) ;
m_fontDictionary . setEntry ( PDFInplaceOrMemoryString ( defaultFontKey ) , defaultFontFactory . takeObject ( ) ) ;
}
m_textFont = PDFFont : : createFont ( fontObject , font , m_document ) ;
2024-05-19 20:13:57 +02:00
return defaultFontKey ;
2024-05-08 17:27:27 +02:00
}
2024-05-19 20:13:57 +02:00
return font ;
2024-05-08 17:27:27 +02:00
}
2024-05-04 16:26:36 +02:00
void PDFPageContentEditorContentStreamBuilder : : addError ( const QString & error )
{
2024-05-19 20:13:57 +02:00
m_errors < < error ;
2024-05-04 16:26:36 +02:00
}
2023-03-06 16:09:09 +01:00
} // namespace pdf