def get_pdf_gpts(m, poly): """ Returns the GPTS array object containing the four corners of the map rect in map projection. The GPTS entry is an array of numbers, taken pairwise, defining points as latitude and longitude. m = mapnik map object poly = tuple of (x,y) tuples describing rect polygon - allows geocoding of rotated maps. """ gpts = ArrayObject() proj = mapnik.Projection(m.srs) for x, y in poly: latlon_corner = proj.inverse(mapnik.Coord(x, y)) # these are in lat,lon order according to the specification gpts.append(FloatObject(str(latlon_corner.y))) gpts.append(FloatObject(str(latlon_corner.x))) return gpts
def get_pdf_measure(m, gcs, poly, bounds_default): """ Returns the PDF Measure dictionary. The Measure dictionary is used in the viewport array and specifies the scale and units that apply to the output map. """ measure = DictionaryObject() measure[NameObject('/Type')] = NameObject('/Measure') measure[NameObject('/Subtype')] = NameObject('/GEO') bounds = ArrayObject() """ Returns the PDF BOUNDS array. The PDF's bounds array is equivalent to the map's neatline, i.e., the border delineating the extent of geographic data on the output map. """ for x in [0, 1, 0, 0, 1, 0, 1, 1]: bounds.append(FloatObject(str(x))) measure[NameObject('/Bounds')] = bounds measure[NameObject('/GPTS')] = get_pdf_gpts(m, poly) measure[NameObject('/LPTS')] = bounds measure[NameObject('/GCS')] = gcs return measure
def merge_page(pdf, page_number: int, page2, page2transformation=None, ctm=None, expand=False): """ Merge the given page number of the given PDF with another page. Note that the first PDF is assumed to be immutable, and its parsed content stream will be cached *indefinitely* to ensure that future renders are extremely fast (at least, as long as the other page, which we never cache, is relatively simple). Note that most of this code was taken from `PyPDF2.pdf.PageObject`'s `mergePage()` implementation. """ # First we work on merging the resource dictionaries. This allows us # to find out what symbols in the content streams we might need to # rename. page1 = pdf.getPage(page_number) newResources = DictionaryObject() rename = {} originalResources = page1["/Resources"].getObject() page2Resources = page2["/Resources"].getObject() newAnnots = ArrayObject() for page in (page1, page2): if "/Annots" in page: annots = page["/Annots"] if isinstance(annots, ArrayObject): for ref in annots: newAnnots.append(ref) for res in ( "/ExtGState", "/Font", "/XObject", "/ColorSpace", "/Pattern", "/Shading", "/Properties", ): new, newrename = PageObject._mergeResources(originalResources, page2Resources, res) if new: newResources[NameObject(res)] = new rename.update(newrename) # Combine /ProcSet sets. newResources[NameObject("/ProcSet")] = ArrayObject( frozenset( originalResources.get("/ProcSet", ArrayObject()).getObject()).union( frozenset( page2Resources.get( "/ProcSet", ArrayObject()).getObject()))) content_stream = get_cached_content_stream(pdf, page_number) page2Content = page2.getContents() if page2Content is not None: if page2transformation is not None: page2Content = page2transformation(page2Content) page2Content = PageObject._contentStreamRename(page2Content, rename, page1.pdf) page2Content = PageObject._pushPopGS(page2Content, page1.pdf) content_stream = append_to_content_stream(content_stream, page2Content) # if expanding the page to fit a new page, calculate the new media box size if expand: corners1 = [ page1.mediaBox.getLowerLeft_x().as_numeric(), page1.mediaBox.getLowerLeft_y().as_numeric(), page1.mediaBox.getUpperRight_x().as_numeric(), page1.mediaBox.getUpperRight_y().as_numeric(), ] corners2 = [ page2.mediaBox.getLowerLeft_x().as_numeric(), page2.mediaBox.getLowerLeft_y().as_numeric(), page2.mediaBox.getUpperLeft_x().as_numeric(), page2.mediaBox.getUpperLeft_y().as_numeric(), page2.mediaBox.getUpperRight_x().as_numeric(), page2.mediaBox.getUpperRight_y().as_numeric(), page2.mediaBox.getLowerRight_x().as_numeric(), page2.mediaBox.getLowerRight_y().as_numeric(), ] if ctm is not None: ctm = [float(x) for x in ctm] new_x = [ ctm[0] * corners2[i] + ctm[2] * corners2[i + 1] + ctm[4] for i in range(0, 8, 2) ] new_y = [ ctm[1] * corners2[i] + ctm[3] * corners2[i + 1] + ctm[5] for i in range(0, 8, 2) ] else: new_x = corners2[0:8:2] new_y = corners2[1:8:2] lowerleft = [min(new_x), min(new_y)] upperright = [max(new_x), max(new_y)] lowerleft = [ min(corners1[0], lowerleft[0]), min(corners1[1], lowerleft[1]) ] upperright = [ max(corners1[2], upperright[0]), max(corners1[3], upperright[1]) ] page1.mediaBox.setLowerLeft(lowerleft) page1.mediaBox.setUpperRight(upperright) new_page = PageObject.createBlankPage(page1.pdf) new_page[NameObject("/Contents")] = content_stream new_page[NameObject("/Resources")] = newResources new_page[NameObject("/Annots")] = newAnnots return new_page
def add_geospatial_pdf_header(m, f, f2, map_bounds, poly, epsg=None, wkt=None): """ Adds geospatial PDF information to the PDF file as per: AdobeĀ® Supplement to the ISO 32000 PDF specification BaseVersion: 1.7 ExtensionLevel: 3 (June 2008) Notes: The epsg code or the wkt text of the projection must be provided. Must be called *after* the page has had .finish() called. """ if not HAS_PYPDF2: raise RuntimeError( "PyPDF2 not available; PyPDF2 required to add geospatial header to PDF" ) if not any((epsg, wkt)): raise RuntimeError( "EPSG or WKT required to add geospatial header to PDF") file_reader = PdfFileReader(f) file_writer = PdfFileWriter() # preserve OCProperties at document root if we have one if NameObject('/OCProperties' ) in file_reader.trailer['/Root']: #Python3-friendly file_writer._root_object[NameObject( '/OCProperties')] = file_reader.trailer['/Root'].getObject()[ NameObject('/OCProperties')] for page in file_reader.pages: gcs = DictionaryObject() gcs[NameObject('/Type')] = NameObject('/PROJCS') if epsg: gcs[NameObject('/EPSG')] = NumberObject(int(epsg)) if wkt: gcs[NameObject('/WKT')] = TextStringObject(wkt) measure = get_pdf_measure(m, gcs, poly, map_bounds) """ Returns the PDF's VP array. The VP entry is an array of viewport dictionaries. A viewport is basiscally a rectangular region on the PDF page. The only required entry is the BBox which specifies the location of the viewport on the page. """ viewport = DictionaryObject() viewport[NameObject('/Type')] = NameObject('/Viewport') bbox = ArrayObject() for x in (0, int(page.mediaBox[3]), int(page.mediaBox[2]), 0): #in pts bbox.append(FloatObject(str(x))) #Fixed viewport[NameObject('/BBox')] = bbox #viewport[NameObject('/Name')] = TextStringObject('OOMAP') viewport[NameObject('/Measure')] = measure vp_array = ArrayObject() vp_array.append(viewport) page[NameObject('/VP')] = vp_array file_writer.addPage(page) file_writer.write(f2) return (f2)
def make_content_stream(pdf, content) -> ContentStream: arr = ArrayObject() arr.append(content) return ContentStream(arr, pdf)