void PdfPagesTree::InsertPagesIntoNode( PdfObject* pParent, const PdfObjectList & rlstParents, int nIndex, const std::vector<PdfObject*>& vecPages ) { if( !pParent || !vecPages.size() ) { PODOFO_RAISE_ERROR( ePdfError_InvalidHandle ); } // 1. Add the reference of the new page to the kids array of pParent // 2. Increase count of every node in lstParents (which also includes pParent) // 3. Add Parent key to the page // 1. Add reference const PdfArray oldKids = pParent->GetDictionary().GetKey( PdfName("Kids") )->GetArray(); PdfArray newKids; newKids.reserve( oldKids.GetSize() + vecPages.size() ); bool bIsPushedIn = false; int i=0; for (PdfArray::const_iterator it=oldKids.begin(); it!=oldKids.end(); ++it, ++i ) { if ( !bIsPushedIn && (nIndex < i) ) // Pushing before { for (std::vector<PdfObject*>::const_iterator itPages=vecPages.begin(); itPages!=vecPages.end(); ++itPages) { newKids.push_back( (*itPages)->Reference() ); // Push all new kids at once } bIsPushedIn = true; } newKids.push_back( *it ); // Push in the old kids } // If new kids are still not pushed in then they may be appending to the end if ( !bIsPushedIn && ( (nIndex + 1) == static_cast<int>(oldKids.size())) ) { for (std::vector<PdfObject*>::const_iterator itPages=vecPages.begin(); itPages!=vecPages.end(); ++itPages) { newKids.push_back( (*itPages)->Reference() ); // Push all new kids at once } bIsPushedIn = true; } pParent->GetDictionary().AddKey( PdfName("Kids"), newKids ); // 2. increase count for ( PdfObjectList::const_reverse_iterator itParents = rlstParents.rbegin(); itParents != rlstParents.rend(); ++itParents ) { this->ChangePagesCount( *itParents, vecPages.size() ); } // 3. add parent key to each of the pages for (std::vector<PdfObject*>::const_iterator itPages=vecPages.begin(); itPages!=vecPages.end(); ++itPages) { (*itPages)->GetDictionary().AddKey( PdfName("Parent"), pParent->Reference() ); } }
void PdfAnnotation::SetBorderStyle( double dHCorner, double dVCorner, double dWidth, const PdfArray & rStrokeStyle ) { // TODO : Support for Border style for PDF Vers > 1.0 PdfArray aValues; aValues.push_back(dHCorner); aValues.push_back(dVCorner); aValues.push_back(dWidth); if( rStrokeStyle.size() ) aValues.push_back(rStrokeStyle); m_pObject->GetDictionary().AddKey( "Border", aValues ); }
void PdfFontCID::CreateWidth( PdfObject* pFontDict ) const { const int cAbsoluteMax = 0xffff; int nFirstChar = m_pEncoding->GetFirstChar(); int nLastChar = m_pEncoding->GetLastChar(); int i; // Allocate an initialize an array, large enough to // hold a width value for every possible glyph index double* pdWidth = static_cast<double*>(malloc( sizeof(double) * cAbsoluteMax ) ); if( !pdWidth ) { PODOFO_RAISE_ERROR( ePdfError_OutOfMemory ); } for( i=0;i<cAbsoluteMax;i++ ) pdWidth[i] = 0.0; // Load the width of all requested glyph indeces int nMin = 0xffff; int nMax = 0; long lGlyph = 0; for( i=nFirstChar;i<=nLastChar;i++ ) { lGlyph = m_pMetrics->GetGlyphId( i ); if( lGlyph ) { nMin = PDF_MIN( static_cast<long>(nMin), lGlyph ); nMax = PDF_MAX( static_cast<long>(nMax), lGlyph ); nMax = PDF_MIN( nMax, cAbsoluteMax ); if( lGlyph < cAbsoluteMax ) pdWidth[lGlyph] = m_pMetrics->GetGlyphWidth( lGlyph ); } } if (nMax >= nMin) { // Now compact the array std::ostringstream oss; PdfArray array; array.reserve( nMax - nMin + 1 ); i = nMin; double dCurWidth = pdWidth[i]; pdf_int64 lCurIndex = i++; pdf_int64 lCurLength = 1L; for( ;i<=nMax;i++ ) { if( static_cast<int>(pdWidth[i] - dCurWidth) == 0 ) ++lCurLength; else { if( lCurLength > 1 ) { array.push_back( lCurIndex ); pdf_int64 temp = lCurIndex + lCurLength - 1; array.push_back( temp ); array.push_back( dCurWidth ); } else { if( array.size() && array.back().IsArray() ) { array.back().GetArray().push_back( dCurWidth ); } else { PdfArray tmp; tmp.push_back( dCurWidth ); array.push_back( lCurIndex ); array.push_back( tmp ); } } lCurIndex = i; lCurLength = 1L; dCurWidth = pdWidth[i]; } } if (array.size() == 0) { array.push_back( lCurIndex = nMin ); array.push_back( lCurIndex = nMax ); array.push_back( dCurWidth ); } pFontDict->GetDictionary().AddKey( PdfName("W"), array ); } free( pdWidth ); }
bool PDFAnalyzer::inspectCanvas(PdfCanvas* canvas, QList<PDFColorSpace> & usedColorSpaces, bool & hasTransparency, QList<PDFFont> & usedFonts, QList<PDFImage> & imgs) { // this method can be used to get used color spaces, detect transparency, and get used fonts in either PdfPage or PdfXObject PdfObject* colorSpaceRes; PdfObject* xObjects; PdfObject* transGroup; PdfObject* extGState; PdfObject* fontRes; QMap<PdfName, PDFColorSpace> processedNamedCS; QMap<PdfName, PDFFont> processedNamedFont; QList<PdfName> processedNamedXObj; QList<PdfName> processedNamedGS; try { // get hold of a PdfObject pointer of this canvas // needed for the finding resources code below to work PdfPage* page = dynamic_cast<PdfPage*>(canvas); PdfObject* canvasObject = page?(page->GetObject()):((dynamic_cast<PdfXObject*>(canvas))->GetObject()); // find a resource with ColorSpace entry PdfObject* resources = canvas->GetResources(); for (PdfObject* par = canvasObject; par && !resources; par = par->GetIndirectKey("Parent")) { resources = par->GetIndirectKey("Resources"); } colorSpaceRes = resources?resources->GetIndirectKey("ColorSpace"):NULL; xObjects = resources?resources->GetIndirectKey("XObject"):NULL; extGState = resources?resources->GetIndirectKey("ExtGState"):NULL; fontRes = resources?resources->GetIndirectKey("Font"):NULL; // getting the transparency group of this content stream (if available) transGroup = canvasObject?canvasObject->GetIndirectKey("Group"):NULL; if (transGroup) { PdfObject* subtype = transGroup->GetIndirectKey("S"); if (subtype && subtype->GetName() == "Transparency") { // having transparency group means there's transparency in the PDF hasTransparency = true; // reporting the color space used in transparency group (Section 7.5.5, PDF 1.6 Spec) PdfObject* cs = transGroup->GetIndirectKey("CS"); if (cs) { PDFColorSpace retval = getCSType(cs); if (retval != CS_Unknown && !usedColorSpaces.contains(retval)) usedColorSpaces.append(retval); } } } } catch (PdfError & e) { qDebug() << "Error in analyzing stream's resources."; e.PrintErrorMsg(); return false; } try { // start parsing the content stream PdfContentsTokenizer tokenizer(canvas); EPdfContentsType t; const char * kwText; PdfVariant var; bool readToken; int tokenNumber = 0; QList<PdfVariant> args; bool inlineImgDict = false; QStack<PDFGraphicState> gsStack; PDFGraphicState currGS; while ((readToken = tokenizer.ReadNext(t, kwText, var))) { ++tokenNumber; if (t == ePdfContentsType_Variant) { args.append(var); } else if (t == ePdfContentsType_Keyword) { QString kw(kwText); switch(kwNameMap.value(kw, KW_Undefined)) { case KW_q: gsStack.push(currGS); break; case KW_Q: currGS = gsStack.pop(); break; case KW_cm: { if (args.size() == 6) { double mt[6]; for (int i=0; i<6; ++i) { mt[i] = args[i].GetReal(); } QMatrix transMatrix(mt[0], mt[1], mt[2], mt[3], mt[4], mt[5]); currGS.ctm = transMatrix*currGS.ctm; } } break; case KW_w: currGS.lineWidth = args[0].GetReal(); break; case KW_J: currGS.lineCap = args[0].GetNumber(); break; case KW_j: currGS.lineJoin = args[0].GetNumber(); break; case KW_M: currGS.lineJoin = args[0].GetReal(); break; case KW_d: { currGS.dashPattern.first.clear(); PdfArray dashArr = args[0].GetArray(); for (int i=0; i<dashArr.size(); ++i) currGS.dashPattern.first.append(dashArr[i].GetNumber()); currGS.dashPattern.second = args[0].GetNumber(); } break; case KW_g: if (!usedColorSpaces.contains(CS_DeviceGray)) usedColorSpaces.append(CS_DeviceGray); currGS.fillCS = CS_DeviceGray; currGS.fillColor.clear(); currGS.fillColor.append(args[0].GetReal()); break; case KW_G: if (!usedColorSpaces.contains(CS_DeviceGray)) usedColorSpaces.append(CS_DeviceGray); currGS.strokeCS = CS_DeviceGray; currGS.strokeColor.clear(); currGS.strokeColor.append(args[0].GetReal()); break; case KW_rg: if (!usedColorSpaces.contains(CS_DeviceRGB)) usedColorSpaces.append(CS_DeviceRGB); currGS.fillCS = CS_DeviceRGB; currGS.fillColor.clear(); for (int i=0; i<args.size(); ++i) currGS.fillColor.append(args[i].GetReal()); break; case KW_RG: if (!usedColorSpaces.contains(CS_DeviceRGB)) usedColorSpaces.append(CS_DeviceRGB); currGS.strokeCS = CS_DeviceRGB; currGS.strokeColor.clear(); for (int i=0; i<args.size(); ++i) currGS.strokeColor.append(args[i].GetReal()); break; case KW_k: if (!usedColorSpaces.contains(CS_DeviceCMYK)) usedColorSpaces.append(CS_DeviceCMYK); currGS.fillCS = CS_DeviceCMYK; currGS.fillColor.clear(); for (int i=0; i<args.size(); ++i) currGS.fillColor.append(args[i].GetReal()); break; case KW_K: if (!usedColorSpaces.contains(CS_DeviceCMYK)) usedColorSpaces.append(CS_DeviceCMYK); currGS.strokeCS = CS_DeviceCMYK; currGS.strokeColor.clear(); for (int i=0; i<args.size(); ++i) currGS.strokeColor.append(args[i].GetReal()); break; case KW_cs: { if (args.size() == 1 && args[0].IsName()) { if (args[0].GetName() == "DeviceGray") { currGS.fillCS = CS_DeviceGray; currGS.fillColor.clear(); currGS.fillColor.append(0); if (!usedColorSpaces.contains(CS_DeviceGray)) usedColorSpaces.append(CS_DeviceGray); } else if (args[0].GetName() == "DeviceRGB") { currGS.fillCS = CS_DeviceRGB; currGS.fillColor.clear(); for (int i=0; i<3; ++i) currGS.fillColor.append(0); if (!usedColorSpaces.contains(CS_DeviceRGB)) usedColorSpaces.append(CS_DeviceRGB); } else if (args[0].GetName() == "DeviceCMYK") { currGS.fillCS = CS_DeviceCMYK; currGS.fillColor.clear(); for (int i=0; i<3; ++i) currGS.fillColor.append(0); currGS.fillColor.append(1); if (!usedColorSpaces.contains(CS_DeviceCMYK)) usedColorSpaces.append(CS_DeviceCMYK); } else if (args[0].GetName() == "Pattern") { currGS.fillCS = CS_Pattern; if (!usedColorSpaces.contains(CS_Pattern)) usedColorSpaces.append(CS_Pattern); } else { if (processedNamedCS.contains(args[0].GetName())) { currGS.fillCS = processedNamedCS.value(args[0].GetName()); } else { if (colorSpaceRes && colorSpaceRes->GetIndirectKey(args[0].GetName())) { PdfObject* csEntry = colorSpaceRes->GetIndirectKey(args[0].GetName()); PDFColorSpace retval = getCSType(csEntry); if (retval != CS_Unknown && !usedColorSpaces.contains(retval)) usedColorSpaces.append(retval); currGS.fillCS = retval; processedNamedCS.insert(args[0].GetName(), retval); } else { qDebug() << "Supplied colorspace is undefined!"; return false; } } } } else { qDebug() << "Wrong syntax in specifying color space!"; return false; } } break; case KW_CS: { if (args.size() == 1 && args[0].IsName()) { if (args[0].GetName() == "DeviceGray") { currGS.strokeCS = CS_DeviceGray; currGS.strokeColor.clear(); currGS.strokeColor.append(0); if (!usedColorSpaces.contains(CS_DeviceGray)) usedColorSpaces.append(CS_DeviceGray); } else if (args[0].GetName() == "DeviceRGB") { currGS.fillCS = CS_DeviceRGB; currGS.strokeColor.clear(); for (int i=0; i<3; ++i) currGS.strokeColor.append(0); if (!usedColorSpaces.contains(CS_DeviceRGB)) usedColorSpaces.append(CS_DeviceRGB); } else if (args[0].GetName() == "DeviceCMYK") { currGS.fillCS = CS_DeviceCMYK; currGS.strokeColor.clear(); for (int i=0; i<3; ++i) currGS.strokeColor.append(0); currGS.strokeColor.append(1); if (!usedColorSpaces.contains(CS_DeviceCMYK)) usedColorSpaces.append(CS_DeviceCMYK); } else if (args[0].GetName() == "Pattern") { currGS.fillCS = CS_Pattern; if (!usedColorSpaces.contains(CS_Pattern)) usedColorSpaces.append(CS_Pattern); } else { if (processedNamedCS.contains(args[0].GetName())) { currGS.strokeCS = processedNamedCS.value(args[0].GetName()); } else { if (colorSpaceRes && colorSpaceRes->GetIndirectKey(args[0].GetName())) { PdfObject* csEntry = colorSpaceRes->GetIndirectKey(args[0].GetName()); PDFColorSpace retval = getCSType(csEntry); if (retval != CS_Unknown && !usedColorSpaces.contains(retval)) usedColorSpaces.append(retval); currGS.strokeCS = retval; processedNamedCS.insert(args[0].GetName(), retval); } else { qDebug() << "Supplied colorspace is undefined!"; return false; } } } } else { qDebug() << "Wrong syntax in specifying color space!"; return false; } } break; case KW_sc: currGS.fillColor.clear(); for (int i=0; i<args.size(); ++i) currGS.fillColor.append(args[i].GetReal()); break; case KW_SC: currGS.strokeColor.clear(); for (int i=0; i<args.size(); ++i) currGS.strokeColor.append(args[i].GetReal()); break; case KW_scn: currGS.fillColor.clear(); for (int i=0; i<args.size(); ++i) { if (args[i].IsReal() || args[i].IsNumber()) currGS.fillColor.append(args[i].GetReal()); } break; case KW_SCN: currGS.strokeColor.clear(); for (int i=0; i<args.size(); ++i) { if (args[i].IsReal() || args[i].IsNumber()) currGS.strokeColor.append(args[i].GetReal()); } break; case KW_Do: // image or form XObject { if (!processedNamedXObj.contains(args[0].GetName())) { if (args.size() == 1 && args[0].IsName() && xObjects) { PdfObject* xObject = xObjects->GetIndirectKey(args[0].GetName()); PdfObject* subtypeObject = xObject?xObject->GetIndirectKey("Subtype"):NULL; if (subtypeObject && subtypeObject->IsName()) { if (subtypeObject->GetName() == "Image") { PdfObject* imgColorSpace = xObject->GetIndirectKey("ColorSpace"); if (imgColorSpace) { PDFColorSpace retval = getCSType(imgColorSpace); if (retval != CS_Unknown && !usedColorSpaces.contains(retval)) usedColorSpaces.append(retval); } PdfObject* sMaskObj = xObject->GetIndirectKey("SMask"); if (sMaskObj) hasTransparency = true; PDFImage img; img.imgName = args[0].GetName().GetEscapedName().c_str(); double width = xObject->GetIndirectKey("Width")->GetReal(); double height = xObject->GetIndirectKey("Height")->GetReal(); img.dpiX = qRound(width/(currGS.ctm.m11()/72)); img.dpiY = qRound(height/(currGS.ctm.m22()/72)); imgs.append(img); } else if (subtypeObject->GetName() == "Form") { PdfXObject xObj(xObject); inspectCanvas(&xObj, usedColorSpaces, hasTransparency, usedFonts, imgs); // recursive call } } else { qDebug() << "Supplied external object is undefined!"; return false; } processedNamedXObj.append(args[0].GetName()); } else { qDebug() << "Wrong syntax for Do operator or there's no XObject defined!"; return false; } } } break; case KW_BI: inlineImgDict = true; break; case KW_ID: if (inlineImgDict) { PdfName colorspace("ColorSpace"); PdfName cs("CS"); if (args.contains(colorspace) || args.contains(cs)) { int csIdx = args.contains(colorspace)?args.indexOf(colorspace):args.indexOf(cs); if (args[csIdx+1].IsName()) { PdfName csName = args[csIdx+1].GetName(); if ((csName == "G" || csName == "DeviceGray") && !usedColorSpaces.contains(CS_DeviceGray)) usedColorSpaces.append(CS_DeviceGray); else if ((csName == "RGB" || csName == "DeviceRGB") && !usedColorSpaces.contains(CS_DeviceRGB)) usedColorSpaces.append(CS_DeviceRGB); else if ((csName == "CMYK" || csName == "DeviceCMYK") && !usedColorSpaces.contains(CS_DeviceCMYK)) usedColorSpaces.append(CS_DeviceCMYK); else if (!processedNamedCS.contains(csName)) { if (colorSpaceRes && colorSpaceRes->GetIndirectKey(csName)) { PdfObject* csEntry = colorSpaceRes->GetIndirectKey(csName); if (csEntry) { PDFColorSpace retval = getCSType(csEntry); if (retval != CS_Unknown && !usedColorSpaces.contains(retval)) usedColorSpaces.append(retval); processedNamedCS.insert(csName, retval); } } else { qDebug() << "Supplied colorspace for inline image is undefined!"; return false; } } } } PdfName height("Height"); PdfName h("H"); PdfName width("Width"); PdfName w("W"); if ((args.contains(height) || args.contains(h)) && (args.contains(width) || args.contains(w))) { int heightIdx = args.contains(height)?args.indexOf(height):args.indexOf(h); int widthIdx = args.contains(width)?args.indexOf(width):args.indexOf(w); double height = args[heightIdx+1].GetReal(); double width = args[widthIdx+1].GetReal(); PDFImage img; img.imgName = "Inline Image"; img.dpiX = qRound(width/(currGS.ctm.m11()/72)); img.dpiY = qRound(height/(currGS.ctm.m22()/72)); imgs.append(img); } inlineImgDict = false; } break; case KW_gs: { if (!processedNamedGS.contains(args[0].GetName())) { if (args.size() == 1 && args[0].IsName() && extGState) { PdfObject* extGStateObj = extGState->GetIndirectKey(args[0].GetName()); if (extGStateObj) { inspectExtGStateObj(extGStateObj, usedColorSpaces, hasTransparency, usedFonts, currGS); } else { qDebug() << "Named graphic state used with gs operator is undefined in current ExtGState"; return false; } processedNamedGS.append(args[0].GetName()); } else { qDebug() << "Wrong syntax in applying extended graphic state (gs operator) or there's no ExtGState defined!"; return false; } } } break; case KW_Tf: { if (processedNamedFont.contains(args[0].GetName())) { currGS.font.first = processedNamedFont.value(args[0].GetName()); currGS.font.second = args[1].GetReal(); } else { if (args.size() == 2 && args[0].IsName() && fontRes) { PdfObject* fontObj = fontRes->GetIndirectKey(args[0].GetName()); if (fontObj) { PDFFont retval = getFontInfo(fontObj); usedFonts.append(retval); processedNamedFont.insert(args[0].GetName(), retval); currGS.font.first = retval; currGS.font.second = args[1].GetReal(); } else { qDebug() << "The specified font cannot be found in current Resources!"; return false; } } else { qDebug() << "Wrong syntax in use of Tf operator or there's no Font defined in current Resources dictionary!"; return false; } } } break; case KW_Undefined: default: break; } args.clear(); } } } catch (PdfError & e) { qDebug() << "Error in parsing content stream"; e.PrintErrorMsg(); return false; } return true; }